mirror of
https://github.com/Kiritow/OJ-Problems-Source.git
synced 2024-03-22 13:11:29 +08:00
Fetch from rsolar/ACM-Template
This commit is contained in:
parent
5ccba6fd68
commit
741ce2b07f
27
.ACM-Templates/分数.cpp
Normal file
27
.ACM-Templates/分数.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//分数类
|
||||
struct Frac {
|
||||
ll a, b; //分子, 分母
|
||||
Frac(const ll &_a = 0, const ll &_b = 1): a(_a), b(_b) {
|
||||
if (b < 0) { a = -a; b = -b; }
|
||||
ll t = __gcd(a, b); a /= t, b /= t;
|
||||
}
|
||||
Frac operator-()const { return Frac(-a, b); }
|
||||
Frac operator+(const Frac &r)const { return Frac(a * r.b + r.a * b, b * r.b); }
|
||||
Frac operator-(const Frac &r)const { return Frac(a * r.b - r.a * b, b * r.b); }
|
||||
Frac operator*(const Frac &r)const { return Frac(a * r.a, b * r.b); }
|
||||
Frac operator/(const Frac &r)const { return Frac(a * r.b, b * r.a); }
|
||||
Frac &operator+=(const Frac &r) { return *this = *this + r; }
|
||||
Frac &operator-=(const Frac &r) { return *this = *this - r; }
|
||||
Frac &operator*=(const Frac &r) { return *this = *this * r; }
|
||||
Frac &operator/=(const Frac &r) { return *this = *this / r; }
|
||||
bool operator<(const Frac &r)const { return a * r.b < r.a * b; }
|
||||
bool operator>(const Frac &r)const { return a * r.b > r.a * b; }
|
||||
bool operator==(const Frac &r)const { return a * r.b == r.a * b; }
|
||||
bool operator!=(const Frac &r)const { return a * r.b != r.a * b; }
|
||||
bool operator<=(const Frac &r)const { return a * r.b <= r.a * b; }
|
||||
bool operator>=(const Frac &r)const { return a * r.b >= r.a * b; }
|
||||
void print() { if (b == 1) { printf("%I64d", a); } else { printf("%I64d/%I64d", a, b); } }
|
||||
friend ostream &operator<<(ostream &out, const Frac &r) {
|
||||
if (r.b == 1) { out << r.a; } else { out << r.a << '/' << r.b; } return out;
|
||||
}
|
||||
};
|
272
.ACM-Templates/动态规划.cpp
Normal file
272
.ACM-Templates/动态规划.cpp
Normal file
|
@ -0,0 +1,272 @@
|
|||
//最大子段和 O(n)
|
||||
ll maxSum(int a[], int n, int &st, int &ed) {
|
||||
ll mx = a[0], mxc = 0; st = ed = 0;
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (mxc > 0) { mxc += a[i]; }
|
||||
else { mxc = a[i]; s = i; }
|
||||
if (mxc > mx) { mx = mxc; st = s; ed = i; }
|
||||
}
|
||||
return mx;
|
||||
}
|
||||
//循环数组最大子段和 O(n)
|
||||
const ll INF = 0x7f7f7f7f7f7fLL;
|
||||
ll maxSum_adj(int a[], int n) {
|
||||
ll mx = -INF, mxc = 0, mn = INF, mnc = 0, sum = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
mxc = a[i] + (mxc > 0 ? mxc : 0);
|
||||
if (mx < mxc) { mx = mxc; }
|
||||
mnc = a[i] + (mnc > 0 ? 0 : mnc);
|
||||
if (mn > mnc) { mn = mnc; }
|
||||
sum += a[i];
|
||||
}
|
||||
return mx < 0 || mx > sum - mn ? mx : sum - mn;
|
||||
}
|
||||
//最大M子段和 O(nm)
|
||||
ll dp[N], mxsum[N];
|
||||
ll mxMSum(int a[], int n, int m) {
|
||||
ll mx;
|
||||
for (int i = 1; i <= m; i++) {
|
||||
mx = 0x8f8f8f8f8f8f8f8fLL;
|
||||
for (int j = i; j <= n; j++) {
|
||||
dp[j] = max(dp[j - 1], mxsum[j - 1]) + a[j];
|
||||
mxsum[j - 1] = mx; mx = max(mx, dp[j]);
|
||||
}
|
||||
}
|
||||
return mx;
|
||||
}
|
||||
//最大子阵和 O(n^3)
|
||||
ll presum[N][N];
|
||||
ll maxSum(int a[][N], int h, int w, int &x1, int &y1, int &x2, int &y2) {
|
||||
ll ret = a[0][0], sum; x1 = y1 = x2 = y2 = 0;
|
||||
for (int i = 0; i < h; i++) {
|
||||
presum[i][0] = 0;
|
||||
for (int j = 0; j < w; j++) { presum[i][j + 1] = presum[i][j] + a[i][j]; }
|
||||
}
|
||||
for (int j = 0; j < w; j++) {
|
||||
for (int k = j, s; k < w; k++) {
|
||||
sum = s = 0;
|
||||
for (int i = 0; i < h; i++, s = sum > 0 ? s : i) {
|
||||
if ((sum = (sum > 0 ? sum : 0) + presum[i][k + 1] - presum[i][j]) > ret) {
|
||||
ret = sum; x1 = s; y1 = j; x2 = i; y2 = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//最长上升子序列 Longest Increasing Subsequence O(nlogn)
|
||||
int b[N];
|
||||
int LIS(int a[], int n) {
|
||||
int len = 1; b[0] = a[0];
|
||||
for (int i = 1; i < n; i++) {
|
||||
b[a[i] > b[len - 1] ? len++ : lower_bound(b, b + len, a[i]) - b] = a[i]; //非降换为>=和upper_bound
|
||||
}
|
||||
return len;
|
||||
}
|
||||
//最长上升子序列数量 O(nlogn)?
|
||||
int b[N], l[N]; ll cnt[N];
|
||||
vector<int> v[N];
|
||||
ll LIS(int a[], int n) {
|
||||
int len = 1; b[0] = a[0]; l[0] = 1; v[1].push_back(0);
|
||||
for (int i = 1; i < n; i++) {
|
||||
int pos = a[i] > b[len - 1] ? len++ : lower_bound(b, b + len, a[i]) - b;
|
||||
b[pos] = a[i]; l[i] = pos + 1;
|
||||
v[l[i]].push_back(i);
|
||||
}
|
||||
ll ret = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (l[i] == 1) { cnt[i] = 1; continue; }
|
||||
for (int j = 0, ll = l[i] - 1; j < (int)v[ll].size() && v[ll][j] <= i; j++) {
|
||||
if (a[v[ll][j]] < a[i]) { cnt[i] += cnt[v[ll][j]]; }
|
||||
}
|
||||
if (l[i] == len) { ret += cnt[i]; }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//长度为k的上升子序列个数 O(knlogn)
|
||||
int n, k;
|
||||
ll bit[M][N];
|
||||
inline int lowbit(int x) { return x & -x; }
|
||||
void add(int id, int i, ll val) { while (i <= n) { bit[id][i] += val; i += lowbit(i); } }
|
||||
ll sum(int id, int i) { ll ret = 0; while (i) { ret += bit[id][i]; i -= lowbit(i); } return ret; }
|
||||
int main() {
|
||||
scanf("%d%d", &n, &k);
|
||||
add(0, 1, 1);
|
||||
for (int i = 1, x; i <= n; i++) {
|
||||
scanf("%d", &x);
|
||||
for (int j = 1; j <= k; j++) { add(j, x, sum(j - 1, x - 1)); } //非降改为x
|
||||
}
|
||||
printf("%I64d\n", sum(k, n)); //n为元素最大值
|
||||
}
|
||||
//最长公共子序列 Longest Common Subsequence O(n^2)
|
||||
int dp[N][N];
|
||||
int LCS(char *a, char *b) {
|
||||
int m = strlen(a), n = strlen(b);
|
||||
for (int i = 1; i <= m; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (a[i - 1] == b[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; }
|
||||
else { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); }
|
||||
}
|
||||
}
|
||||
return dp[m][n];
|
||||
}
|
||||
void printLCS(char *a, char *b) {
|
||||
char s[N] = {0};
|
||||
for (int i = strlen(a), j = strlen(b), k = dp[i][j]; i && j;) {
|
||||
if (a[i - 1] == b[j - 1] && dp[i][j] == dp[i - 1][j - 1] + 1) { s[--k] = a[--i]; --j; }
|
||||
else if (dp[i - 1][j] > dp[i][j - 1]) { i--; }
|
||||
else { j--; }
|
||||
}
|
||||
puts(s);
|
||||
}
|
||||
//最长公共子串 Longest Common Substring
|
||||
//DP O(n^2)
|
||||
int dp[2][N];
|
||||
int LCS_dp(char *a, char *b, int &st1, int &st2) {
|
||||
int m = strlen(a), n = strlen(b), ret = 0, cur = 0; st1 = st2 = -1;
|
||||
for (int i = 0; i < m; i++, cur ^= 1) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (a[i] == b[j]) {
|
||||
dp[cur][j] = i == 0 || j == 0 ? 1 : dp[cur ^ 1][j - 1] + 1;
|
||||
if (dp[cur][j] > ret) { ret = dp[cur][j]; st1 = i + 1 - ret; st2 = j + 1 - ret; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//outputLCS(a, ret, st1);
|
||||
return ret;
|
||||
}
|
||||
//后缀数组 O(nlogn)
|
||||
char *suf[N];
|
||||
int pstrcmp(const void *p, const void *q) {
|
||||
return strcmp(*(char **)p, *(char **)q);
|
||||
}
|
||||
int comlen_suf(const char *p, const char *q) {
|
||||
for (int len = 0; *p && *q && *p++ == *q++;) {
|
||||
len++;
|
||||
if (*p == '#' || *q == '#') { return len; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int LCS_suffix(char *a, char *b) {
|
||||
int m = strlen(a), n = strlen(b), ret = 0, suf_index = 0, len_suf = m + n + 1;
|
||||
char *arr = new char[len_suf + 1];
|
||||
strcpy(arr, a); arr[m] = '#'; strcpy(arr + m + 1, b);
|
||||
for (int i = 0; i < len_suf; i++) { suf[i] = &arr[i]; }
|
||||
qsort(suf, len_suf, sizeof(char *), pstrcmp);
|
||||
for (int i = 0; i < len_suf - 1; i++) {
|
||||
int len = comlen_suf(suf[i], suf[i + 1]);
|
||||
if (len > ret) { ret = len; suf_index = i; }
|
||||
}
|
||||
//outputLCS(suf[suf_index], ret);
|
||||
delete[] arr; return ret;
|
||||
}
|
||||
void outputLCS(char *s, int len, int i = 0) {
|
||||
for (; len--; i++) { putchar(s[i]); } puts("");
|
||||
}
|
||||
//DP 下界O(nlogn) 上界O(nmlog(nm))
|
||||
int c[N * N], d[N * N];
|
||||
int LCS_dp(char *a, char *b) {
|
||||
vector<int> pos[26]; int k = 0, len = 1; d[0] = c[0];
|
||||
for (int i = strlen(b) - 1; i >= 0; i--) { pos[b[i] - 'a'].push_back(i); }
|
||||
for (int i = 0; a[i]; i++) {
|
||||
for (int j = 0; j < (int)pos[a[i] - 'a'].size(); j++) { c[k++] = pos[a[i] - 'a'][j]; }
|
||||
}
|
||||
for (int i = 1; i < k; i++) {
|
||||
d[c[i] > d[len - 1] ? len++ : lower_bound(d, d + len, c[i]) - d] = c[i];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
//最长公共递增子序列 GCIS O(n^2)
|
||||
int dp[N], f[N][N];
|
||||
int GCIS(int a[], int la, int b[], int lb, int ans[]) {
|
||||
//a[1...la], b[1...lb]
|
||||
int mx = 0;
|
||||
memset(f, 0, sizeof(f));
|
||||
memset(dp, 0, sizeof(dp));
|
||||
for (int i = 1; i <= la; i++) {
|
||||
memcpy(f[i], f[i - 1], sizeof(f[0]));
|
||||
for (int j = 1, k = 0; j <= lb; j++) {
|
||||
if (a[j - 1] > b[i - 1] && dp[k] < dp[j]) { k = j; }
|
||||
if (b[j - 1] == a[i - 1] && dp[k] + 1 > dp[j]) { dp[j] = dp[k] + 1; f[i][j] = i * (lb + 1) + k; }
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= lb; i++) { if (dp[i] > dp[mx]) { mx = i; } }
|
||||
for (int i = la * lb + la + mx, j = dp[mx]; j; i = f[i / (lb + 1)][i % (lb + 1)], j--) {
|
||||
ans[j - 1] = b[i % (lb + 1) - 1];
|
||||
}
|
||||
return dp[mx];
|
||||
}
|
||||
//字符串编辑距离 (Levenshtein距离)
|
||||
//操作包括将替换一个字符, 插入一个字符, 删除一个字符
|
||||
int dp[N][N];
|
||||
int LevDist(char *a, char *b) {
|
||||
int n = strlen(a), m = strlen(b);
|
||||
for (int i = 0; i <= n; i++) { dp[i][0] = i; }
|
||||
for (int i = 0; i <= m; i++) { dp[0][i] = i; }
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
|
||||
if (a[i - 1] == b[j - 1]) { dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]); }
|
||||
else { dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1); }
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
//字符串距离
|
||||
//非空格字符的距离定义为它们的ASCII码的差的绝对值, 空格字符与其它任意字符之间的距离为已知的定值k
|
||||
int dp[N][N];
|
||||
int dist(char *a, char *b, int k) {
|
||||
int n = strlen(a), m = strlen(b);
|
||||
for (int i = 0; i <= n; i++) { dp[i][0] = i * k; }
|
||||
for (int i = 1; i <= m; i++) { dp[0][i] = i * k; }
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[i][j] = min(dp[i - 1][j - 1] + abs(a[i - 1] - b[j - 1]), min(dp[i - 1][j], dp[i][j - 1]) + k);
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
//TSP问题 O(V^2*2^V)
|
||||
int n, mp[N][N], dp[1 << N][N];
|
||||
int TSP() {
|
||||
memset(dp, 0x1f, sizeof(dp));
|
||||
dp[1][0] = 0;
|
||||
for (int s = 0; s < 1 << n; s++) {
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (dp[s][v] == 0x1f1f1f1f) { continue; }
|
||||
for (int u = 0; u < n; u++) {
|
||||
if (s & 1 << u) { continue; }
|
||||
dp[s | 1 << u][u] = min(dp[s | 1 << u][u], dp[s][v] + mp[v][u]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int ans = 0x1f1f1f1f;
|
||||
for (int i = 0; i < n; i++) { ans = min(ans, dp[(1 << n) - 1][i] + mp[i][0]); }
|
||||
return ans;
|
||||
}
|
||||
//mTSP问题 O(V^2*2^V)
|
||||
int n, mp[N][N], dp[1 << N][N], best[1 << N];
|
||||
bool ok[1 << N]; //该集合状态是否可行
|
||||
int mTSP() {
|
||||
memset(dp, 0x1f, sizeof(dp));
|
||||
memset(best, 0x1f, sizeof(best));
|
||||
dp[1][0] = 0;
|
||||
for (int s = 0; s < 1 << n; s++) {
|
||||
if (!ok[s]) { continue; }
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (!(s & (1 << v)) || dp[s][v] == 0x1f1f1f1f) { continue; }
|
||||
best[s] = min(best[s], dp[s][v] + mp[v][0]);
|
||||
for (int u = 0; u < n; u++) {
|
||||
if (s & (1 << u)) { continue; }
|
||||
dp[s | 1 << u][u] = min(dp[s | 1 << u][u], dp[s][v] + mp[v][u]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int s = 0; s < 1 << n; s++) {
|
||||
if (!(s & 1)) { continue; }
|
||||
for (int i = s & (s - 1); i; i = s & (i - 1)) {
|
||||
best[s] = min(best[s], best[i] + best[(s ^ i) | 1]);
|
||||
}
|
||||
}
|
||||
return best[(1 << n) - 1];
|
||||
}
|
70
.ACM-Templates/博弈.cpp
Normal file
70
.ACM-Templates/博弈.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
//威佐夫博弈 Wizov game
|
||||
//有两堆各若干个物品, 两个人轮流从某一堆或同时从两堆中取同样多的物品, 规定每次至少取一个, 多者不限, 最后取光者得胜
|
||||
//ak = [k(1 + √ 5) / 2], bk = ak + k (k = 0, 1, 2, ..., n 方括号表示取整函数)
|
||||
const double gs = (sqrt(5.0) + 1.0) / 2.0;
|
||||
bool Wizov(ll a, ll b) {
|
||||
return min(a, b) == (ll)(abs(a - b) * gs);
|
||||
}
|
||||
//威佐夫博弈 1 <= N <= 1e18
|
||||
const int N = 95; //~95 for 1e18
|
||||
ll fib[N] = { 0, 1 }; //预处理fibonacci数列
|
||||
bool s[N];
|
||||
bool Wizov(ll a, ll b) {
|
||||
int w = upper_bound(fib + 1, fib + N, a) - fib - 1, pos = 1; ll ret = 0;
|
||||
for (int i = w; i > 0; i--) {
|
||||
if (a >= fib[i]) { s[i] = true; a -= fib[i]; }
|
||||
else { s[i] = false; }
|
||||
}
|
||||
while (!s[pos]) { pos++; }
|
||||
for (int i = pos & 1 ? w - 2 : w; i >= 0; i--) {
|
||||
if (s[i]) { ret += fib[i + 1]; }
|
||||
}
|
||||
return ret == b;
|
||||
}
|
||||
//尼姆博奕 Nimm Game
|
||||
//有三堆各若干个物品, 两个人轮流从某一堆取任意多的物品, 规定每次至少取一个, 多者不限, 最后取光者得胜
|
||||
//计算从1 - n范围内的SG值
|
||||
//Array存储可以走的步数, Array[0]表示可以有多少种走法
|
||||
//Array[]需要从小到大排序
|
||||
//HDU1847 博弈SG函数
|
||||
//1.可选步数为1-m的连续整数,直接取模即可,SG(x) = x % (m + 1); (即巴什博奕 Bash game)
|
||||
//2.可选步数为任意步,SG(x) = x;
|
||||
//3.可选步数为一系列不连续的数,用SG(x)计算
|
||||
int sg[N];
|
||||
bool Hash[N];
|
||||
int SG(int Array[], int n) {
|
||||
memset(sg, 0, sizeof(sg));
|
||||
for (int i = 0; i <= n; ++i) {
|
||||
memset(Hash, 0, sizeof(Hash));
|
||||
for (int j = 1; j <= Array[0]; j++) {
|
||||
if (i < Array[j]) { break; }
|
||||
Hash[sg[i - Array[j]]] = true;
|
||||
}
|
||||
for (int j = 0; j <= n; j++) {
|
||||
if (!Hash[j]) { sg[i] = j; break; }
|
||||
}
|
||||
}
|
||||
return sg[n];
|
||||
}
|
||||
//带输出方案
|
||||
int a[N], ans[N][2]; //a[]为各堆石子数量
|
||||
void printNim(int n) { //石子堆数
|
||||
int cnt = 0, ret = 0;
|
||||
for (int i = 0; i < n; i++) { ret ^= a[i]; }
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (a[i] > (ret ^ a[i])) {
|
||||
ans[cnt][0] = a[i]; ans[cnt][1] = s ^ a[i];
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt) { //判断先手是胜是负
|
||||
puts("Yes");
|
||||
printf("%d\n", cnt); //输出使先手为胜的方案的数目
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
printf("%d %d\n", ans[i][0], ans[i][1]); //输出若先手为胜的走法
|
||||
}
|
||||
} else { puts("No"); }
|
||||
}
|
||||
//树上删边游戏
|
||||
//给定一棵n个点的有根树, 每次可以删掉一个子树
|
||||
//则叶子节点的SG值为0, 非叶子节点的SG值为其所有孩子节点(SG值 + 1)的异或和
|
88
.ACM-Templates/图论-LCA.cpp
Normal file
88
.ACM-Templates/图论-LCA.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
//LCA (Least Common Ancestors, 最近公共祖先)
|
||||
//在线dfs + ST O(nlogn + q)
|
||||
int dfso[N << 1], cnt; //欧拉序列, 即dfs序, 长度为2n-1, 下标从1开始
|
||||
int pos[N]; //pos[i]表示点i在欧拉序列中第一次出现的位置
|
||||
int dep[N << 1]; //欧拉序列对应的深度
|
||||
int p[N << 1] = { -1}, dp[N << 1][20];
|
||||
void initRMQ(int n) {
|
||||
for (int i = 1; i <= n; i++) { p[i] = p[i - 1] + !(i & (i - 1)); dp[i][0] = i; }
|
||||
for (int j = 1; j <= p[n]; j++) {
|
||||
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
|
||||
dp[i][j] = dep[dp[i][j - 1]] < dep[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
inline int queryRMQ(int l, int r) {
|
||||
if (l > r) { swap(l, r); }
|
||||
int k = p[r - l + 1];
|
||||
return dep[dp[l][k]] <= dep[dp[r - (1 << k) + 1][k]] ? dp[l][k] : dp[r - (1 << k) + 1][k];
|
||||
}
|
||||
void dfs(int u, int p, int d) {
|
||||
dfso[++cnt] = u; dep[cnt] = d; pos[u] = cnt;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (v != p) { dfs(v, u, d + 1); dfso[++cnt] = u; dep[cnt] = d; }
|
||||
}
|
||||
}
|
||||
void initLCA(int rt, int n) {
|
||||
cnt = 0; dfs(rt, rt, 0); initRMQ(2 * n - 1);
|
||||
}
|
||||
inline int queryLCA(int u, int v) { //查询u和v的LCA编号
|
||||
return dfso[queryRMQ(pos[u], pos[v])];
|
||||
}
|
||||
//倍增法 O(nlogn + qlogn)
|
||||
const int DEP = 20;
|
||||
int fa[N][DEP]; //fa[i][j]表示结点i的第2^j个祖先
|
||||
int dep[N]; //深度
|
||||
void bfs(int rt) {
|
||||
queue<int> que; que.push(rt);
|
||||
dep[rt] = 0; fa[rt][0] = rt;
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int i = 1; i < DEP; i++) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; }
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (v != fa[u][0]) { dep[v] = dep[u] + 1; fa[v][0] = u; que.push(v); }
|
||||
}
|
||||
}
|
||||
}
|
||||
int queryLCA(int u, int v) {
|
||||
if (dep[u] > dep[v]) { swap(u, v); }
|
||||
for (int d = dep[v] - dep[u], i = 0; d; d >>= 1, i++) {
|
||||
if (d & 1) { v = fa[v][i]; }
|
||||
}
|
||||
if (u == v) { return u; }
|
||||
for (int i = DEP - 1; i >= 0; i--) {
|
||||
if (fa[u][i] != fa[v][i]) { u = fa[u][i]; v = fa[v][i]; }
|
||||
}
|
||||
return fa[u][0];
|
||||
}
|
||||
//离线Tarjan O(n + q)
|
||||
int n, fa[N], ancestor[N], ans[N];
|
||||
bool vis[N];
|
||||
int qhead[N], qto[M], qnxt[M], qtot;
|
||||
void init() {
|
||||
qtot = 0; memset(qhead, -1, sizeof(qhead));
|
||||
memset(ancestor, 0, sizeof(ancestor)); memset(vis, 0, sizeof(vis));
|
||||
for (int i = 0; i <= n; i++) { fa[i] = i; }
|
||||
}
|
||||
void qaddedge(int x, int y) {
|
||||
qto[qtot] = y; qnxt[qtot] = qhead[x]; qhead[x] = qtot++;
|
||||
qto[qtot] = x; qnxt[qtot] = qhead[y]; qhead[y] = qtot++;
|
||||
}
|
||||
int findfa(int n) { return n == fa[n] ? n : fa[n] = findfa(fa[n]); }
|
||||
inline void unite(int x, int y) {
|
||||
x = findfa(x); y = findfa(y);
|
||||
if (x != y) { fa[y] = x; }
|
||||
}
|
||||
void Tarjan_LCA(int u) {
|
||||
ancestor[u] = u; vis[u] = true;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!vis[v]) { Tarjan_LCA(v); unite(u, v); ancestor[findfa(u)] = u; }
|
||||
}
|
||||
for (int i = qhead[u]; ~i; i = qnxt[i]) {
|
||||
int v = qto[i];
|
||||
if (vis[v]) { ans[i >> 1] = ancestor[findfa(v)]; }
|
||||
}
|
||||
}
|
357
.ACM-Templates/图论-匹配.cpp
Normal file
357
.ACM-Templates/图论-匹配.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
//图论点、边集和二分图的相关概念和性质
|
||||
//点覆盖: 点覆盖集即一个点集, 使得所有边至少有一个端点在集合里
|
||||
//边覆盖: 边覆盖集即一个边集, 使得所有点都与集合里的边邻接
|
||||
//独立集: 独立集即一个点集, 集合中任两个结点不相邻, 则称V为独立集
|
||||
//团: 团即一个点集, 集合中任两个结点相邻
|
||||
//边独立集: 边独立集即一个边集, 满足边集中的任两边不邻接
|
||||
//支配集: 支配集即一个点集, 使得所有其他点至少有一个相邻点在集合里
|
||||
//边支配集: 边支配集即一个边集, 使得所有边至少有一条邻接边在集合里
|
||||
//最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的所有顶点, 即每个顶点严格属于一条路径, 路径的长度可能为0(单个点)
|
||||
//匹配: 匹配是一个边集, 满足边集中的边两两不邻接
|
||||
//在匹配中的点称为匹配点或饱和点;反之, 称为未匹配点或未饱和点
|
||||
//交错轨(alternating path): 图的一条简单路径, 满足任意相邻的两条边, 一条在匹配内, 一条不在匹配内
|
||||
//增广轨(augmenting path): 是一个始点与终点都为未匹配点的交错轨
|
||||
//最大匹配(maximum matching): 具有最多边的匹配
|
||||
//匹配数(matching number): 最大匹配的大小
|
||||
//完美匹配(perfect matching): 匹配了所有点的匹配
|
||||
//完备匹配(complete matching): 匹配了二分图较小集合(二分图X, Y中小的那个)的所有点的匹配
|
||||
//增广轨定理: 一个匹配是最大匹配当且仅当没有增广轨
|
||||
//所有匹配算法都是基于增广轨定理: 一个匹配是最大匹配当且仅当没有增广轨
|
||||
//最大匹配数 = 最小覆盖数 = 左边匹配点 + 右边未匹配点
|
||||
//最小边覆盖 = 图中点的个数 - 最大匹配数 = 独立数
|
||||
|
||||
//二分图最大匹配
|
||||
//Hungary + dfs
|
||||
//邻接矩阵 O(V^2)
|
||||
int uN, vN, match[N]; //左右点的数目
|
||||
bool g[N][N], check[N];
|
||||
bool dfs(int u) {
|
||||
for (int v = 0; v < vN; v++) {
|
||||
if (g[u][v] && !check[v]) {
|
||||
check[v] = true;
|
||||
if (match[v] == -1 || dfs(match[v])) {
|
||||
match[v] = u; match[u] = v; return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//邻接表 O(V*E)
|
||||
int head[N], to[M], nxt[M], tot, uN, match[N];
|
||||
bool check[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
bool dfs(int u) {
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!check[v]) {
|
||||
check[v] = true;
|
||||
if (match[v] == -1 || dfs(match[v])) {
|
||||
match[v] = u; match[u] = v; return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int Hungary() {
|
||||
memset(match, -1, sizeof(match));
|
||||
int res = 0;
|
||||
for (int u = 0; u < uN; u++) {
|
||||
if (match[u] == -1) {
|
||||
memset(check, 0, sizeof(check));
|
||||
if (dfs(u)) { res++; }
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//Hungary + bfs + 邻接矩阵 O(V*E)
|
||||
bool g[N][N];
|
||||
int uN, vN, match[N], check[N], pre[N];
|
||||
int Hungary() {
|
||||
memset(match, -1, sizeof(match));
|
||||
memset(check, -1, sizeof(check));
|
||||
queue<int> que;
|
||||
int res = 0;
|
||||
for (int i = 0; i < uN; i++) {
|
||||
if (match[i] == -1) {
|
||||
while (!que.empty()) { que.pop(); }
|
||||
que.push(i); pre[i] = -1;
|
||||
bool flag = false;
|
||||
while (!que.empty() && !flag) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int v = 0; v < vN && !flag; v++) {
|
||||
if (g[u][v] && check[v] != i) {
|
||||
check[v] = i; que.push(match[v]);
|
||||
if (~match[v]) { pre[match[v]] = u; }
|
||||
else {
|
||||
flag = true;
|
||||
for (int a = u, b = v; ~a;) {
|
||||
int t = match[a]; match[a] = b; match[b] = a; a = pre[a]; b = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (~match[i]) { res++; }
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//Hungary + bfs + 邻接表 O(V*E)
|
||||
int head[N], to[M], nxt[M], tot, uN, match[N], check[N], pre[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
int Hungary() {
|
||||
memset(match, -1, sizeof(match));
|
||||
memset(check, -1, sizeof(check));
|
||||
queue<int> que;
|
||||
int res = 0;
|
||||
for (int i = 0; i < uN; i++) {
|
||||
if (match[i] == -1) {
|
||||
while (!que.empty()) { que.pop(); }
|
||||
que.push(i); pre[i] = -1;
|
||||
bool flag = false;
|
||||
while (!que.empty() && !flag) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int i = head[u]; ~i && !flag; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (check[v] != i) {
|
||||
check[v] = i; que.push(match[v]);
|
||||
if (~match[v]) { pre[match[v]] = u; }
|
||||
else {
|
||||
flag = true;
|
||||
for (int a = u, b = v; ~a;) {
|
||||
int t = match[a]; match[a] = b; match[b] = a; a = pre[a]; b = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (~match[i]) { res++; }
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//Hopcroft-Karp + vector + O(sqrt(V)*E)
|
||||
const int INF = 0x3f3f3f3f;
|
||||
vector<int> g[N];
|
||||
int uN, matchx[N], matchy[N], dx[N], dy[N], dis;
|
||||
bool check[N];
|
||||
bool SearchP() {
|
||||
memset(dx, -1, sizeof(dx));
|
||||
memset(dy, -1, sizeof(dy));
|
||||
queue<int> que; dis = INF;
|
||||
for (int i = 0; i < uN; i++) { if (matchx[i] == -1) { dx[i] = 0; que.push(i); } }
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
if (dx[u] > dis) { break; }
|
||||
for (size_t i = 0; i < g[u].size(); i++) {
|
||||
int v = g[u][i];
|
||||
if (dy[v] == -1) {
|
||||
dy[v] = dx[u] + 1;
|
||||
if (matchy[v] == -1) { dis = dy[v]; }
|
||||
else { dx[matchy[v]] = dy[v] + 1; que.push(matchy[v]); }
|
||||
}
|
||||
}
|
||||
}
|
||||
return dis != INF;
|
||||
}
|
||||
bool dfs(int u) {
|
||||
for (size_t i = 0; i < g[u].size(); i++) {
|
||||
int v = g[u][i];
|
||||
if (!check[v] && dy[v] == dx[u] + 1) {
|
||||
check[v] = true;
|
||||
if (~matchy[v] && dy[v] == dis) { continue; }
|
||||
if (matchy[v] == -1 || dfs(matchy[v])) {
|
||||
matchy[v] = u; matchx[u] = v; return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int HK() {
|
||||
memset(matchx, -1, sizeof(matchx));
|
||||
memset(matchy, -1, sizeof(matchy));
|
||||
int res = 0;
|
||||
while (SearchP()) {
|
||||
memset(check, 0, sizeof(check));
|
||||
for (int i = 0; i < uN; i++) {
|
||||
if (matchx[i] == -1 && dfs(i)) { res++; }
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//二分图最佳匹配
|
||||
//Kuhn-Munkers + 邻接矩阵 O(uN^2*vN)
|
||||
//若求最小权匹配,可将权值取相反数,结果取相反数 点的编号从0开始
|
||||
const int INF = 0x3f3f3f3f;
|
||||
int uN, vN, g[N][N];
|
||||
int matchy[N], lx[N], ly[N]; //y中各点匹配状态, 可行顶标
|
||||
int slack[N]; //松弛数组
|
||||
bool visx[N], visy[N];
|
||||
bool dfs(int u) {
|
||||
visx[u] = true;
|
||||
for (int v = 0; v < vN; v++) {
|
||||
if (visy[v]) { continue; }
|
||||
int tmp = lx[u] + ly[v] - g[u][v];
|
||||
if (tmp == 0) {
|
||||
visy[v] = true;
|
||||
if (matchy[v] == -1 || dfs(matchy[v])) { matchy[v] = u; return true; }
|
||||
} else if (slack[v] > tmp) { slack[v] = tmp; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int KM() {
|
||||
memset(matchy, -1, sizeof(matchy));
|
||||
memset(ly, 0, sizeof(ly));
|
||||
for (int i = 0; i < uN; i++) { lx[i] = *max_element(g[i], g[i] + vN); }
|
||||
for (int u = 0; u < uN; u++) {
|
||||
memset(slack, 0x3f, sizeof(slack));
|
||||
for (;;) {
|
||||
memset(visx, 0, sizeof(visx));
|
||||
memset(visy, 0, sizeof(visy));
|
||||
if (dfs(u)) { break; }
|
||||
int d = INF;
|
||||
for (int i = 0; i < vN; i++) { if (!visy[i] && d > slack[i]) { d = slack[i]; } }
|
||||
for (int i = 0; i < uN; i++) { if (visx[i]) { lx[i] -= d; } }
|
||||
for (int i = 0; i < vN; i++) { if (visy[i]) { ly[i] += d; } else { slack[i] -= d; } }
|
||||
}
|
||||
}
|
||||
int res = 0;
|
||||
for (int i = 0; i < vN; i++) { if (matchy[i] != -1) { res += g[matchy[i]][i]; } }
|
||||
return res;
|
||||
}
|
||||
//二分图多重匹配
|
||||
int uN, vN, g[N][N], match[N][N]; //match[i][0]为个数
|
||||
int num[N]; //右边最大的匹配数
|
||||
bool check[N];
|
||||
bool dfs(int u) {
|
||||
for (int v = 0; v < vN; v++) {
|
||||
if (g[u][v] && !check[v]) {
|
||||
check[v] = true;
|
||||
if (match[v][0] < num[v]) { match[v][++match[v][0]] = u; return true; }
|
||||
for (int i = 1; i <= num[0]; i++) {
|
||||
if (dfs(match[v][i])) { match[v][i] = u; return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int Hungary() {
|
||||
int res = 0;
|
||||
for (int i = 0; i < vN; i++) { match[i][0] = 0; }
|
||||
for (int u = 0; u < uN; u++) {
|
||||
memset(check, false, sizeof(check));
|
||||
res += dfs(u);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//一般图最大匹配
|
||||
//邻接矩阵 O(V^3)
|
||||
int n, match[N];
|
||||
bool g[N][N], check[N];
|
||||
bool aug(int now) {
|
||||
bool ret = false; check[now] = true;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!check[i] && g[now][i]) {
|
||||
if (match[i] == -1) { match[now] = i; match[i] = now; ret = true; }
|
||||
else {
|
||||
check[i] = true;
|
||||
if (aug(match[i])) { match[now] = i, match[i] = now, ret = true; }
|
||||
check[i] = false;
|
||||
}
|
||||
if (ret) { break; }
|
||||
}
|
||||
}
|
||||
check[now] = false; return ret;
|
||||
}
|
||||
//邻接表 O(V*E)
|
||||
int head[N], to[M], nxt[M], tot, match[N];
|
||||
bool check[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
bool aug(int now) {
|
||||
bool ret = false; check[now] = true;
|
||||
for (int i = head[now]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!check[v]) {
|
||||
if (match[v] == -1) { match[now] = v; match[v] = now; ret = true; }
|
||||
else {
|
||||
check[v] = true;
|
||||
if (aug(match[v])) { match[now] = v; match[v] = now; ret = true; }
|
||||
check[v] = false;
|
||||
}
|
||||
if (ret) { break; }
|
||||
}
|
||||
}
|
||||
check[now] = false; return ret;
|
||||
}
|
||||
int graphMatch() {
|
||||
memset(match, -1, sizeof(match));
|
||||
memset(check, 0, sizeof(check));
|
||||
for (int i = 0, j = n; i < n && j >= 2; i++) {
|
||||
if (match[i] == -1 && aug(i)) { i = -1, j -= 2; }
|
||||
}
|
||||
int ret = 0;
|
||||
for (int i = 0; i < n; i++) { ret += (match[i] != -1); }
|
||||
return ret >> 1;
|
||||
}
|
||||
//带花树 + vector O(V^3) 点的编号从0开始
|
||||
int n, match[N], fa[N], nxt[N], mark[N], vis[N], t;
|
||||
vector<int> e[N]; queue<int> que;
|
||||
int findfa(int n) { return n == fa[n] ? n : fa[n] = findfa(fa[n]); }
|
||||
void unite(int x, int y) { fa[findfa(x)] = findfa(y); }
|
||||
int lca(int x, int y) {
|
||||
for (t++;; swap(x, y)) {
|
||||
if (x == -1) { continue; }
|
||||
if (vis[x = findfa(x)] == t) { return x; }
|
||||
vis[x] = t; x = match[x] == -1 ? -1 : nxt[match[x]];
|
||||
}
|
||||
}
|
||||
void group(int a, int p) {
|
||||
for (int b, c; a != p; unite(a, b), unite(b, c), a = c) {
|
||||
b = match[a]; c = nxt[b];
|
||||
if (findfa(c) != p) { nxt[c] = b; }
|
||||
if (mark[b] == 2) { mark[b] = 1; que.push(b); }
|
||||
if (mark[c] == 2) { mark[c] = 1; que.push(c); }
|
||||
}
|
||||
}
|
||||
void aug(int st) {
|
||||
for (int i = 0; i < n; i++) { fa[i] = i; }
|
||||
memset(nxt, -1, sizeof(nxt)); memset(vis, -1, sizeof(vis)); memset(mark, 0, sizeof(mark));
|
||||
while (!que.empty()) { que.pop(); } que.push(st); mark[st] = 1;
|
||||
while (match[st] == -1 && !que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
int v = e[u][i];
|
||||
if (v == match[u] || findfa(u) == findfa(v) || mark[v] == 2) { continue; }
|
||||
if (mark[v] == 1) {
|
||||
int p = lca(u, v);
|
||||
if (findfa(u) != p) { nxt[u] = v; }
|
||||
if (findfa(v) != p) { nxt[v] = u; }
|
||||
group(u, p); group(v, p);
|
||||
} else if (match[v] == -1) {
|
||||
nxt[v] = u;
|
||||
for (int j = v, k, l; ~j; j = l) { k = nxt[j]; l = match[k]; match[j] = k; match[k] = j; }
|
||||
break;
|
||||
} else { nxt[v] = u; que.push(match[v]); mark[match[v]] = 1; mark[v] = 2; }
|
||||
}
|
||||
}
|
||||
}
|
||||
int solve(int n) {
|
||||
memset(match, -1, sizeof(match)); t = 0; int ret = 0;
|
||||
for (int i = 0; i < n; i++) { if (match[i] == -1) { aug(i); } }
|
||||
for (int i = 0; i < n; i++) { ret += (match[i] > i); }
|
||||
return ret;
|
||||
}
|
||||
//ural 1099
|
||||
int main() {
|
||||
int u, v;
|
||||
scanf("%d", &n);
|
||||
while (~scanf("%d%d", &u, &v)) { e[--u].push_back(--v); e[v].push_back(u); }
|
||||
int ans = solve(n);
|
||||
printf("%d\n", ans * 2);
|
||||
for (int u = 0; u < n; u++) {
|
||||
if (u < match[u]) { printf("%d %d\n", u + 1, match[u] + 1); }
|
||||
}
|
||||
}
|
430
.ACM-Templates/图论-应用.cpp
Normal file
430
.ACM-Templates/图论-应用.cpp
Normal file
|
@ -0,0 +1,430 @@
|
|||
//拓扑排序 O(V^2)
|
||||
//queue->普通判断用, priority_queue->字典序
|
||||
//邻接矩阵
|
||||
int n, mp[N][N], in[N], ret[N];
|
||||
int topoSort() {
|
||||
queue<int> que; int k = 0; bool flag = false;
|
||||
for (int i = 0; i < n; i++) { if (in[i] == 0) { que.push(i); } }
|
||||
while (!que.empty()) {
|
||||
if (que.size() > 1) { flag = true; }
|
||||
int u = que.front(); que.pop(); ret[k++] = u;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (mp[u][v] && --in[v] == 0) { que.push(v); }
|
||||
}
|
||||
}
|
||||
return k < n ? -1 : flag ? 0 : 1; //有环, 不唯一, 唯一
|
||||
}
|
||||
//邻接表
|
||||
int head[N], to[M], nxt[M], tot, n, in[N], ret[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
bool topoSort() {
|
||||
priority_queue<int> que; int k = 0;
|
||||
for (int i = 0; i < n; i++) { if (in[i] == 0) { que.push(i); } }
|
||||
while (!que.empty()) {
|
||||
int u = que.top(); que.pop(); ret[k++] = u;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
if (--in[to[i]] == 0) { que.push(to[i]); }
|
||||
}
|
||||
}
|
||||
return k == n;
|
||||
}
|
||||
//染色判断二分图
|
||||
bool col[N];
|
||||
bool Color(int u) {
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!col[v]) {
|
||||
col[v] = !col[u];
|
||||
if (!Color(v)) { return false; }
|
||||
} else if (col[v] == col[u]) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//欧拉回路: 每条边只经过一次, 要求回到起点
|
||||
//欧拉路径: 每条边只经过一次, 不要求回到起点
|
||||
//欧拉路径的判断:
|
||||
//无向图: 连通(不考虑度为0的点), 每个顶点度数都为偶数或者有且仅有两个点的度数不为偶数
|
||||
//有向图: 基图连通(把边当成无向边, 同样不考虑度为0的点), 每个顶点出度等于入度
|
||||
//或者有且仅有一个点的出度比入度多1, 有且仅有一个点的出度比入度少1, 其余出度等于入度
|
||||
//混合图: 如果存在欧拉回路, 一点存在欧拉路径了。否则如果有且仅有两个点的(出度 - 入度)是奇数,
|
||||
//那么给这个两个点加边, 判断是否存在欧拉回路
|
||||
//欧拉回路判断:
|
||||
//无向图: 连通(不考虑度为0的点), 每个顶点度数都为偶数
|
||||
//有向图: 基图连通(同样不考虑度为0的点), 每个顶点出度等于入度
|
||||
//混合图: 基图连通(不考虑度为0的点), 然后借助网络流判定
|
||||
//首先给原图中的每条无向边随便指定一个方向, 将原图改为有向图G'
|
||||
//设D[i]为G'中(点i的出度 - 点i的入度), 若存在D[i]为奇数, 或者图不连通, 则无解
|
||||
//若初始D值都是偶数, 则将G'改装成网络: 设立源点S和汇点T
|
||||
//对于每个D[i] > 0的点i, 连边<S, i>, 容量为D[i] / 2; 对于每个D[j] < 0的点j, 连边<j, T>, 容量为-D[j] / 2
|
||||
//G'中的每条边在网络中仍保留, 容量为1, 求这个网络的最大流, 若S引出的所有边均满流, 则原混合图是欧拉图
|
||||
//将网络中所有流量为1的不与S或T关联的边在G'中改变方向,形成的新图G''一定是有向欧拉图
|
||||
//欧拉回路 + 邻接矩阵 O(N^2)
|
||||
//求欧拉路径/回路经过的点 支持自环和重边
|
||||
int n, mp[N][N], path[N], cnt;
|
||||
void dfsu(int u) {
|
||||
for (int v = n - 1; v >= 0; v--) {
|
||||
while (mp[u][v]) { mp[u][v]--; mp[v][u]--; dfsu(v); }
|
||||
}
|
||||
path[cnt++] = u;
|
||||
}
|
||||
void dfsd(int u) {
|
||||
for (int v = n - 1; v >= 0; v--) {
|
||||
while (mp[u][v]) { mp[u][v]--; dfsd(v); }
|
||||
}
|
||||
path[cnt++] = u;
|
||||
}
|
||||
//无向图 SGU101
|
||||
int head[N], to[M], nxt[M], tot, deg[N], path[M], cnt;
|
||||
bool vis[M];
|
||||
void init() {
|
||||
tot = 0; cnt = 0;
|
||||
memset(head, -1, sizeof(head)); memset(vis, 0, sizeof(vis)); memset(deg, 0, sizeof(deg));
|
||||
}
|
||||
void addedge(int x, int y) {
|
||||
to[tot] = y; nxt[tot] = head[x]; head[x] = tot++;
|
||||
to[tot] = x; nxt[tot] = head[y]; head[y] = tot++;
|
||||
}
|
||||
void dfs(int u) {
|
||||
for (int &i = head[u]; ~i;) {
|
||||
if (!vis[i]) { vis[i] = vis[i ^ 1] = true; int t = i; dfs(to[i]); path[cnt++] = t; }
|
||||
else { i = nxt[i]; }
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
int n, u, v;
|
||||
while (~scanf("%d", &n)) {
|
||||
init();
|
||||
for (int i = 0; i < n; i++) {
|
||||
scanf("%d%d", &u, &v); addedge(u, v); deg[u]++; deg[v]++;
|
||||
}
|
||||
int s = -1, cnto = 0;
|
||||
for (int i = 0; i <= 6; i++) {
|
||||
if (s == -1 && deg[i] > 0) { s = i; }
|
||||
if (deg[i] & 1) { cnto++; s = i; }
|
||||
}
|
||||
if (cnto != 0 && cnto != 2) { puts("No solution"); continue; }
|
||||
dfs(s);
|
||||
if (cnt != n) { puts("No solution"); continue; }
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
printf("%d %c\n", (path[i] >> 1) + 1, path[i] & 1 ? '+' : '-');
|
||||
}
|
||||
}
|
||||
}
|
||||
//有向图 POJ2337
|
||||
//给出n个小写字母组成的单词, 要求将n个单词连接起来, 输出字典序最小的解
|
||||
int head[N], to[M], nxt[M], tot, in[N], out[N], path[N], cnt;
|
||||
bool vis[M];
|
||||
string str[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) {
|
||||
to[tot] = y; nxt[tot] = head[x]; head[x] = tot++;
|
||||
}
|
||||
void dfs(int u) {
|
||||
for (int &i = head[u]; ~i;) {
|
||||
if (!vis[i]) { vis[i] = true; int t = i; dfs(to[i]); path[cnt++] = n - t - 1; }
|
||||
else { i = nxt[i]; }
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
int T, n;
|
||||
char s[25];
|
||||
scanf("%d", &T);
|
||||
while (T--) {
|
||||
init(); cnt = 0;
|
||||
memset(vis, 0, sizeof(vis));
|
||||
memset(in, 0, sizeof(in));
|
||||
memset(out, 0, sizeof(out));
|
||||
scanf("%d", &n);
|
||||
for (int i = 0; i < n; i++) { scanf("%s", s); str[i] = s; }
|
||||
sort(str, str + n);
|
||||
int s = 26;
|
||||
for (int i = n - 1; i >= 0; i--) { //字典序大的先加入
|
||||
int u = str[i][0] - 'a', v = str[i][str[i].size() - 1] - 'a';
|
||||
addedge(u, v); out[u]++; in[v]++;
|
||||
s = min(s, min(u, v));
|
||||
}
|
||||
int cnt1 = 0, cnt2 = 0;
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (out[i] - in[i] == 1) { cnt1++; s = i; } //如果有一个出度比入度大1的点, 就从这个点出发, 否则从最小的点出发
|
||||
else if (out[i] - in[i] == -1) { cnt2++; }
|
||||
else if (out[i] - in[i] != 0) { cnt1 = 3; }
|
||||
}
|
||||
if (!((cnt1 == 0 && cnt2 == 0) || (cnt1 == 1 && cnt2 == 1))) { puts("***"); continue; }
|
||||
dfs(s);
|
||||
if (cnt != n) { puts("***"); continue; }
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
printf("%s%c", str[path[i]].c_str(), i > 0 ? '.' : '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
//有向图 并查集 HDU1116
|
||||
//判断所有单词能不能连成一串, 如果有多个重复的单词, 也必须满足这样的条件
|
||||
int n, fa[N], in[N], out[N], p[N];
|
||||
bool vis[N];
|
||||
char s[M];
|
||||
void init() {
|
||||
memset(vis, 0, sizeof(vis)); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out));
|
||||
for (int i = 0; i < N; i++) { fa[i] = i; }
|
||||
}
|
||||
int findfa(int n) {
|
||||
return n == fa[n] ? n : fa[n] = findfa(fa[n]);
|
||||
}
|
||||
inline void unite(int x, int y) {
|
||||
x = findfa(x); y = findfa(y);
|
||||
if (x != y) { fa[y] = x; }
|
||||
}
|
||||
int main() {
|
||||
int T, n;
|
||||
scanf("%d", &T);
|
||||
while (T--) {
|
||||
init();
|
||||
scanf("%d", &n);
|
||||
while (n--) {
|
||||
scanf("%s", s); int u = s[0] - 'a', v = s[strlen(s) - 1] - 'a';
|
||||
unite(u, v); out[u]++; in[v]++; vis[u] = vis[v] = true;
|
||||
}
|
||||
int cnt = 0, k = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
fa[i] = findfa(i);
|
||||
if (vis[i] && fa[i] == i) { cnt++; }
|
||||
}
|
||||
if (cnt > 1) { puts("The door cannot be opened."); continue; } //不连通
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (in[i] != out[i]) { p[k++] = i; if (k > 2) { break; } }
|
||||
}
|
||||
if (k == 0 || (k == 2 && ((out[p[0]] - in[p[0]] == 1 && in[p[1]] - out[p[1]] == 1) ||
|
||||
(out[p[1]] - in[p[1]] == 1 && in[p[0]] - out[p[0]] == 1)))) {
|
||||
puts("Ordering is possible.");
|
||||
} else { puts("The door cannot be opened."); }
|
||||
}
|
||||
}
|
||||
//混合图 POJ1637 (本题保证连通)
|
||||
//判断欧拉回路 需ISAP + 邻接表 O(V^2*E)
|
||||
int in[N], out[N];
|
||||
int main() {
|
||||
int T, u, v, w;
|
||||
scanf("%d", &T);
|
||||
while (T--) {
|
||||
init();
|
||||
memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out));
|
||||
scanf("%d%d", &n, &m);
|
||||
while (m--) {
|
||||
scanf("%d%d%d", &u, &v, &w); out[u]++; in[v]++;
|
||||
if (w == 0) { addedge(u, v, 1); } //双向
|
||||
}
|
||||
bool flag = true;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
if (out[i] - in[i] > 0) { addedge(0, i, (out[i] - in[i]) >> 1); }
|
||||
else if (in[i] - out[i] > 0) { addedge(i, n + 1, (in[i] - out[i]) >> 1); }
|
||||
if (out[i] - in[i] & 1) { flag = false; break; }
|
||||
}
|
||||
if (!flag) { puts("impossible"); continue; }
|
||||
ISAP(0, n + 1, n + 2);
|
||||
for (int i = head[0]; ~i; i = nxt[i]) {
|
||||
if (cap[i] > 0 && cap[i] > flow[i]) { flag = false; break; }
|
||||
}
|
||||
puts(flag ? "possible" : "impossible");
|
||||
}
|
||||
}
|
||||
//2-SAT
|
||||
//染色法
|
||||
const int N = 20005;
|
||||
const int M = 100005;
|
||||
int head[N], to[M], nxt[M], tot;
|
||||
bool vis[N]; //染色标记
|
||||
int S[N], top; //栈
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
bool dfs(int u) {
|
||||
if (vis[u ^ 1]) { return false; }
|
||||
if (vis[u]) { return true; }
|
||||
vis[u] = true; S[top++] = u;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
if (!dfs(to[i])) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool twoSAT(int n) {
|
||||
memset(vis, 0, sizeof(vis));
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
if (vis[i] || vis[i ^ 1]) { continue; }
|
||||
top = 0;
|
||||
if (!dfs(i)) {
|
||||
while (top) { vis[S[--top]] = false; }
|
||||
if (!dfs(i ^ 1)) { return false; }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//HDU 1814
|
||||
int main() {
|
||||
int n, m, u, v;
|
||||
while (~scanf("%d%d", &n, &m)) {
|
||||
init();
|
||||
while (m--) {
|
||||
scanf("%d%d", &u, &v); u--; v--;
|
||||
addedge(u, v ^ 1); addedge(v, u ^ 1);
|
||||
}
|
||||
if (twoSAT(n << 1)) {
|
||||
for (int i = 0; i < n << 1; i++) {
|
||||
if (vis[i]) { printf("%d\n", i + 1); }
|
||||
}
|
||||
} else { printf("NIE\n"); }
|
||||
}
|
||||
}
|
||||
//Tarjan强连通缩点
|
||||
const int N = 1005;
|
||||
const int M = 100005;
|
||||
int head[N], to[M], nxt[M], tot;
|
||||
int num[N], Low[N], DFN[N], S[N], Belong[N], idx, top, scc; //Belong数组的值1~scc
|
||||
bool instack[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y) { to[tot] = y; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
void Tarjan(int u) {
|
||||
Low[u] = DFN[u] = ++idx; S[top++] = u; instack[u] = true;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!DFN[v]) { Tarjan(v); Low[u] = min(Low[u], Low[v]); }
|
||||
else if (instack[v] && Low[u] > DFN[v]) { Low[u] = DFN[v]; }
|
||||
}
|
||||
if (Low[u] == DFN[u]) {
|
||||
scc++;
|
||||
do { v = S[--top]; instack[v] = false; Belong[v] = scc; num[scc]++; } while (v != u);
|
||||
}
|
||||
}
|
||||
bool solvable(int n) { //n是总个数, 需要选择一半
|
||||
memset(DFN, 0, sizeof(DFN));
|
||||
memset(instack, 0, sizeof(instack));
|
||||
memset(num, 0, sizeof(num));
|
||||
idx = scc = top = 0;
|
||||
for (int i = 0; i < n; i++) { if (!DFN[i]) { Tarjan(i); } }
|
||||
for (int i = 0; i < n; i += 2) { if (Belong[i] == Belong[i ^ 1]) { return false; } }
|
||||
return true;
|
||||
}
|
||||
//拓扑排序求任意一组解部分
|
||||
queue<int> q1, q2;
|
||||
vector<vector<int>> dag; //缩点后的逆向DAG图
|
||||
char color[N]; //染色, 为'R'是选择的
|
||||
int cf[N], indeg[N]; //入度
|
||||
void solve(int n) {
|
||||
dag.assign(scc + 1, vector<int>());
|
||||
memset(indeg, 0, sizeof(indeg));
|
||||
memset(color, 0, sizeof(color));
|
||||
for (int u = 0; u < n; u++) {
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (Belong[u] != Belong[v]) { dag[Belong[v]].push_back(Belong[u]); indeg[Belong[u]]++; }
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
cf[Belong[i]] = Belong[i ^ 1]; cf[Belong[i ^ 1]] = Belong[i];
|
||||
}
|
||||
while (!q1.empty()) { q1.pop(); }
|
||||
while (!q2.empty()) { q2.pop(); }
|
||||
for (int i = 1; i <= scc; i++) { if (indeg[i] == 0) { q1.push(i); } }
|
||||
while (!q1.empty()) {
|
||||
int u = q1.front(); q1.pop();
|
||||
if (color[u] == 0) { color[u] = 'R'; color[cf[u]] = 'B'; }
|
||||
for (int i = 0; i < (int)dag[u].size(); i++) {
|
||||
if (--indeg[dag[u][i]] == 0) { q1.push(dag[u][i]); }
|
||||
}
|
||||
}
|
||||
}
|
||||
int change(char s[]) {
|
||||
int ret = 0, i = 0;
|
||||
while (s[i] >= '0' && s[i] <= '9') { ret *= 10; ret += s[i++] - '0'; }
|
||||
return (ret << 1) + (s[i] != 'w');
|
||||
}
|
||||
//POJ3648
|
||||
int main() {
|
||||
int n, m, u, v;
|
||||
char s1[10], s2[10];
|
||||
while (scanf("%d%d", &n, &m), (n || m)) {
|
||||
init();
|
||||
while (m--) {
|
||||
scanf("%s%s", s1, s2); u = change(s1); v = change(s2);
|
||||
addedge(u ^ 1, v); addedge(v ^ 1, u);
|
||||
}
|
||||
addedge(1, 0);
|
||||
if (solvable(n << 1)) {
|
||||
solve(n << 1);
|
||||
for (int i = 1; i < n; i++) {
|
||||
//注意这一定是判断color[Belong[
|
||||
if (color[Belong[i << 1]] == 'R') { printf("%dw", i); }
|
||||
else { printf("%dh", i); }
|
||||
putchar(i != n - 1 ? ' ' : '\n');
|
||||
}
|
||||
} else { printf("bad luck\n"); }
|
||||
}
|
||||
}
|
||||
//最大团
|
||||
//搜索 O(n*2^n)
|
||||
int mp[N][N], stk[N][N], dp[N], ans;
|
||||
bool dfs(int crt, int tot) {
|
||||
if (!crt) {
|
||||
if (tot > ans) { ans = tot; return true; }
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, u, nxt; i < crt; i++) {
|
||||
u = stk[tot][i]; nxt = 0;
|
||||
if (crt - i + tot <= ans) { return false; }
|
||||
if (dp[u] + tot <= ans) { return false; }
|
||||
for (int j = i + 1; j < crt; j++) {
|
||||
int v = stk[tot][j];
|
||||
if (g[u][v]) { stk[tot + 1][nxt++] = v; }
|
||||
}
|
||||
if (dfs(nxt, tot + 1)) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int maxClique(int n) {
|
||||
ans = 0;
|
||||
for (int i = n - 1, j, k; i >= 0; i--) {
|
||||
for (j = i + 1, k = 0; j < n; j++) {
|
||||
if (g[i][j]) { stk[1][k++] = j; }
|
||||
}
|
||||
dfs(k, 1); dp[i] = ans;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
//随机贪心 O(T*n^2)
|
||||
const int T = 1000;
|
||||
int mp[N][N], id[N], ansn, ans[N]; bool del[N];
|
||||
void solve(int n) {
|
||||
memset(del, 0, sizeof(del)); int k = 0;
|
||||
for (int i = 0, j; i < n; i++) {
|
||||
if (del[i]) { continue; }
|
||||
for (j = i + 1, k++; j < n; j++) { if (!mp[id[i]][id[j]]) { del[j] = true; } }
|
||||
}
|
||||
if (k > ansn) {
|
||||
ansn = k;
|
||||
for (int i = k = 0; i < n; i++) { if (!del[i]) { ans[k++] = id[i]; } }
|
||||
}
|
||||
}
|
||||
void maxClique(int n) {
|
||||
for (int i = 0; i < n; i++) { id[i] = i; }
|
||||
for (int t = 0; t < T; t++) {
|
||||
for (int i = 0; i < n; i++) { swap(id[i], id[rand() % n]); } solve();
|
||||
}
|
||||
}
|
||||
//最大独立集
|
||||
//随机算法 O(T*(V+E))
|
||||
const int T = 1000;
|
||||
int q[N], pos[N]; bool del[N];
|
||||
int solve(int n) {
|
||||
int ans = 0;
|
||||
for (int t = 0; t < T; t++) {
|
||||
int ret = 0, top = n; memset(del, 0, sizeof(del));
|
||||
for (int i = 1; i <= n; i++) { q[i] = pos[i] = i; }
|
||||
while (top) {
|
||||
int x = rand() % top + 1, u = q[x]; q[x] = q[top--]; pos[q[x]] = x; ret++;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!del[v]) { del[v] = true; x = pos[v]; q[x] = q[top--]; pos[q[x]] = x; }
|
||||
}
|
||||
}
|
||||
ans = max(ans, ret);
|
||||
}
|
||||
return ans;
|
||||
}
|
225
.ACM-Templates/图论-最短路.cpp
Normal file
225
.ACM-Templates/图论-最短路.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
//邻接表
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
int head[N], to[M], nxt[M], tot; wtype len[M];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y, wtype z) { to[tot] = y; len[tot] = z; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
//Dijkstra + priority_queue + 邻接矩阵 O(V^2)
|
||||
typedef int wtype;
|
||||
struct Node {
|
||||
int v; wtype w;
|
||||
bool operator<(const Node &r)const { return w > r.w; }
|
||||
};
|
||||
int n, pre[N]; wtype mp[N][N], dist[N]; bool vis[N]; //邻接矩阵初始化为INF
|
||||
void Dijkstra(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist));
|
||||
memset(vis, 0, sizeof(vis)); memset(pre, -1, sizeof(pre));
|
||||
priority_queue<Node> que; que.push((Node) {src, dist[src] = 0});
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; que.pop();
|
||||
if (vis[u]) { continue; }
|
||||
vis[u] = true;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (!vis[v] && dist[v] > dist[u] + mp[u][v]) {
|
||||
dist[v] = dist[u] + mp[u][v]; pre[v] = u; que.push((Node) {v, dist[v]});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Dijkstra + priority_queue + vector存边 O((V+E)logV)
|
||||
typedef int wtype;
|
||||
struct Node {
|
||||
int v; wtype w;
|
||||
bool operator<(const Node &r)const { return w > r.w; }
|
||||
};
|
||||
vector<Node> e[N]; wtype dist[N]; bool vis[N];
|
||||
void Dijkstra(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist));
|
||||
memset(vis, 0, sizeof(vis));
|
||||
priority_queue<Node> que; que.push((Node) {src, dist[src] = 0});
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; que.pop();
|
||||
if (vis[u]) { continue; }
|
||||
vis[u] = true;
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
int v = e[u][i].v; wtype w = e[u][i].w;
|
||||
if (!vis[v] && dist[v] > dist[u] + w) {
|
||||
dist[v] = dist[u] + w; que.push((Node) {v, dist[v]});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Dijkstra + priority_queue + 邻接表 O((V+E)logV)
|
||||
typedef int wtype;
|
||||
struct Node {
|
||||
int v; wtype w;
|
||||
bool operator<(const Node &r)const { return w > r.w; }
|
||||
};
|
||||
wtype dist[N];
|
||||
void Dijkstra(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist));
|
||||
priority_queue<Node> que; que.push((Node) {src, dist[src] = 0});
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; wtype w = que.top().w; que.pop();
|
||||
if (w > dist[u]) { continue; }
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (dist[v] > len[i] + w) {
|
||||
dist[v] = w + len[i]; que.push((Node) {v, dist[v]});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//SPFA + queue/stack + vector存边 O(kE)
|
||||
typedef int wtype;
|
||||
struct Edge { int v; wtype w; };
|
||||
vector<Edge> e[N]; int cnt[N], pre[N]; wtype dist[N]; bool vis[N];
|
||||
bool SPFA(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist)); dist[src] = 0;
|
||||
memset(cnt, 0, sizeof(cnt)); cnt[src] = 1;
|
||||
memset(vis, 0, sizeof(vis)); vis[src] = true;
|
||||
memset(pre, -1, sizeof(pre));
|
||||
queue<int> que; que.push(src); //stack
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
vis[u] = false;
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
int v = e[u][i].v; wtype w = e[u][i].w;
|
||||
if (dist[v] > dist[u] + w) {
|
||||
dist[v] = dist[u] + w; pre[v] = u;
|
||||
if (!vis[v]) {
|
||||
vis[v] = true; que.push(v);
|
||||
if (++cnt[v] > n) { return false; } //有负环回路
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; //没有负环回路
|
||||
}
|
||||
//SPFA + SLF + LLL + 邻接表 O(kE)
|
||||
typedef int wtype;
|
||||
wtype dist[N];
|
||||
bool vis[M];
|
||||
void SPFA(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist)); dist[src] = 0;
|
||||
memset(vis, 0, sizeof(vis)); vis[src] = true;
|
||||
deque<int> que; que.push_back(src);
|
||||
ll sum = 0;
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop_front();
|
||||
if (!que.empty() && (ll)dist[u] * que.size() > sum) { que.push_back(u); continue; }
|
||||
sum -= dist[u]; vis[u] = false;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i]; wtype d = dist[u] + len[i];
|
||||
if (d < dist[v]) {
|
||||
if (vis[v]) { sum += d - dist[v]; }
|
||||
dist[v] = d;
|
||||
if (!vis[v]) {
|
||||
if (!que.empty() && dist[que.front()] > dist[v]) { que.push_front(v); }
|
||||
else { que.push_back(v); }
|
||||
sum += dist[v]; vis[v] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//差分约束系统
|
||||
//a向b连一条权值为c的有向边表示b − a <= c, 用SPFA判断是否存在负环, 存在即无解
|
||||
//Bellman-Ford + vector O(VE)
|
||||
//可以处理负边权图, 可以判断是否存在负环回路, 当且仅当图中不包含从源点可达的负权回路时返回true
|
||||
typedef int wtype;
|
||||
struct Edge { int u, v; wtype w; };
|
||||
vector<Edge> e;
|
||||
wtype dist[N];
|
||||
bool BellmanFord(int src) {
|
||||
memset(dist, 0x3f, sizeof(dist)); dist[src] = 0;
|
||||
for (int i = 1; i < n; i++) {
|
||||
bool flag = false;
|
||||
for (int j = 0; j < (int)e.size(); j++) {
|
||||
int u = e[j].u, v = e[j].v; wtype w = e[j].w;
|
||||
if (dist[v] > dist[u] + w) { dist[v] = dist[u] + w; flag = true; }
|
||||
}
|
||||
if (!flag) { return true; } //没有负环回路
|
||||
}
|
||||
for (int j = 0; j < (int)e.size(); j++) {
|
||||
int u = e[j].u, v = e[j].v; wtype w = e[j].w;
|
||||
if (dist[v] > dist[u] + w) { return false; } //有负环回路
|
||||
}
|
||||
return true; //没有负环回路
|
||||
}
|
||||
//Floyd 带路径记录 O(V^3)
|
||||
typedef int wtype;
|
||||
int n, pre[N][N]; wtype mp[N][N]; //初始化为INF
|
||||
void Floyd() {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) { pre[i][j] = (i == j) ? -1 : i; }
|
||||
}
|
||||
for (int k = 1; k <= n; k++) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
if (mp[i][k] + mp[k][j] < mp[i][j]) {
|
||||
mp[i][j] = mp[i][k] + mp[k][j]; pre[i][j] = pre[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//无负权图的最小环
|
||||
//有向图: mp[i][i] = INF, 然后跑floyd, ans = min(d[i][i])
|
||||
//求无向图中经过至少3个点的最小环:
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
int n; wtype mp[N][N], d[N][N]; //初始化为INF
|
||||
wtype cycFloyd() {
|
||||
memcpy(d, mp, sizeof(mp)); wtype ret = INF;
|
||||
for (int k = 1; k <= n; k++) {
|
||||
for (int i = 1; i < k; i++) {
|
||||
for (int j = i + 1; j < k; j++) {
|
||||
ans = min(ans, d[i][j] + mp[i][k] + mp[k][j]);
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= n; j++) {
|
||||
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Astar求k短路
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
int head[N], to[M], nxt[M], tot; wtype len[M];
|
||||
int rhead[N], rto[M], rnxt[M], rtot; wtype rlen[M];
|
||||
void init() { tot = rtot = 0; memset(head, -1, sizeof(head)); memset(rhead, -1, sizeof(rhead)); }
|
||||
void addedge(int x, int y, wtype z) {
|
||||
to[tot] = y; len[tot] = z; nxt[tot] = head[x]; head[x] = tot++;
|
||||
rto[rtot] = x; rlen[rtot] = z; rnxt[rtot] = rhead[y]; rhead[y] = rtot++;
|
||||
}
|
||||
struct Node {
|
||||
int v; wtype w;
|
||||
bool operator<(const Node &r)const { return w > r.w; }
|
||||
};
|
||||
int vis[N]; wtype dist[N], ans[N]; //前k短路
|
||||
void Astar(int src, int des, int k) {
|
||||
memset(dist, 0x3f, sizeof(dist)); memset(ans, 0x3f, sizeof(ans)); memset(vis, 0, sizeof(vis));
|
||||
priority_queue<Node> que; que.push((Node) {des, dist[des] = 0});
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; wtype w = que.top().w; que.pop();
|
||||
if (w > dist[u]) { continue; }
|
||||
for (int i = rhead[u]; ~i; i = rnxt[i]) {
|
||||
int v = rto[i];
|
||||
if (dist[v] > rlen[i] + w) {
|
||||
dist[v] = w + rlen[i]; que.push((Node) {v, dist[v]});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dist[src] < INF) { que.push((Node) {src, dis[src]}); }
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; wtype w = que.top().w; que.pop();
|
||||
if (u == des && vis[des] <= k) { ans[vis[des]] = w; }
|
||||
if (vis[u] > k) { continue; }
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i]; que.push((Node) {v, w - dist[u] + dist[v] + len[i]});
|
||||
}
|
||||
}
|
||||
}
|
289
.ACM-Templates/图论-生成树.cpp
Normal file
289
.ACM-Templates/图论-生成树.cpp
Normal file
|
@ -0,0 +1,289 @@
|
|||
//最小生成树
|
||||
//Prim + 邻接矩阵 O(V^2)
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
int n; wtype mp[N][N], cost[N];
|
||||
bool vis[N];
|
||||
wtype Prim(int src) {
|
||||
memset(vis, 0, sizeof(vis)); memcpy(cost, mp[src], sizeof(mp[src]));
|
||||
wtype ret = 0; vis[src] = true;
|
||||
for (int j = 1; j < n; j++) {
|
||||
int u = -1; wtype mn = INF;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!vis[i] && mn > cost[i]) { mn = cost[i]; u = i; }
|
||||
}
|
||||
if (mn == INF) { return -1; } //原图不连通
|
||||
vis[u] = true; ret += mn;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (!vis[v] && cost[v] > mp[u][v]) { cost[v] = mp[u][v]; }
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//Prim + priority_queue + 邻接表 O(ElogV)
|
||||
typedef int wtype;
|
||||
int head[N], to[M], nxt[M], tot; wtype len[M], cost[N];
|
||||
bool vis[N];
|
||||
void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
void addedge(int x, int y, wtype z) { to[tot] = y; len[tot] = z; nxt[tot] = head[x]; head[x] = tot++; }
|
||||
struct Node {
|
||||
int v; wtype w;
|
||||
bool operator<(const Node &r)const { return w > r.w; }
|
||||
};
|
||||
wtype Prim(int src) {
|
||||
memset(cost, 0x3f, sizeof(cost)); memset(vis, 0, sizeof(vis));
|
||||
wtype ret = 0;
|
||||
priority_queue<Node> que; que.push((Node) {src, cost[src] = 0});
|
||||
while (!que.empty()) {
|
||||
int u = que.top().v; que.pop();
|
||||
if (vis[u]) { continue; }
|
||||
vis[u] = true; ret += cost[u];
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (!vis[v] && cost[v] > len[i]) { cost[v] = len[i]; que.push((Node) {v, cost[v]}); }
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//Kruskal + 邻接表 O(ElogE)
|
||||
typedef int wtype;
|
||||
struct Edge {
|
||||
int u, v; wtype w;
|
||||
bool operator<(const Edge &r)const { return w < r.w; }
|
||||
} edge[M];
|
||||
int n, fa[N], tot; //加边前赋值为0
|
||||
void addedge(int u, int v, wtype w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
|
||||
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
|
||||
wtype Kruskal() {
|
||||
memset(fa, -1, sizeof(fa));
|
||||
sort(edge, edge + tot);
|
||||
int cnt = 0; wtype ret = 0;
|
||||
for (int i = 0; i < tot; i++) {
|
||||
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
|
||||
if (t1 != t2) { ret += w; fa[t1] = t2; cnt++; }
|
||||
if (cnt == n - 1) { break; }
|
||||
}
|
||||
if (cnt < n - 1) { return -1; } //不连通
|
||||
return ret;
|
||||
}
|
||||
//次小生成树
|
||||
//Prim + 邻接矩阵 O(V^2 + E)
|
||||
//求最小生成树时, 用数组Mx[i][j]来表示MST中i到j最大边权
|
||||
//求完后, 直接枚举所有不在MST中的边, 替换掉最大边权的边, 更新答案
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
int n, pre[N];
|
||||
wtype mp[N][N], cost[N], Mx[N][N]; //Mx[i][j]表示在最小生成树中从i到j的路径中的最大边权
|
||||
bool vis[N], used[N][N];
|
||||
wtype Prim() {
|
||||
memset(Mx, 0, sizeof(Mx));
|
||||
memset(pre, 0, sizeof(pre));
|
||||
memset(vis, 0, sizeof(vis));
|
||||
memset(used, 0, sizeof(used));
|
||||
wtype ret = 0; vis[0] = true; pre[0] = -1;
|
||||
for (int i = 1; i < n; i++) { cost[i] = mp[0][i]; }
|
||||
for (int j = 1; j < n; j++) {
|
||||
int u = -1; wtype mn = INF;
|
||||
for (int j = 0; j < n; j++)
|
||||
if (!vis[j] && mn > cost[j]) { mn = cost[j]; u = j; }
|
||||
if (mn == INF) { return -1; } //原图不连通
|
||||
vis[u] = true; ret += mn;
|
||||
used[u][pre[u]] = used[pre[u]][u] = true;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (vis[v]) { Mx[v][u] = Mx[u][v] = max(Mx[v][pre[u]], cost[u]); }
|
||||
if (!vis[v] && cost[v] > mp[u][v]) { cost[v] = mp[u][v]; pre[v] = u; }
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//Kruskal + 邻接表 O(VElogE)
|
||||
typedef int wtype;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
struct Edge {
|
||||
int u, v; wtype w;
|
||||
bool operator<(const Edge &r)const { return w < r.w; }
|
||||
} edge[M];
|
||||
int n, fa[N], path[N], tot; //加边前赋值为0
|
||||
void addedge(int u, int v, wtype w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
|
||||
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
|
||||
wtype Kruskal() {
|
||||
memset(fa, -1, sizeof(fa));
|
||||
sort(edge, edge + tot);
|
||||
int cnt = 0; wtype ret = 0;
|
||||
for (int i = 0; i < tot; i++) {
|
||||
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
|
||||
if (t1 != t2) { ret += w; fa[t1] = t2; path[cnt++] = i; }
|
||||
if (cnt == n - 1) { break; }
|
||||
}
|
||||
if (cnt < n - 1) { return -1; } //不连通
|
||||
return ret;
|
||||
}
|
||||
wtype KruskalSec() {
|
||||
wtype ret = INF;
|
||||
for (int x = 0; x < n - 1; x++) {
|
||||
memset(fa, -1, sizeof(fa));
|
||||
int cnt = 0; wtype tmp = 0;
|
||||
for (int i = 0; i < tot; i++) {
|
||||
if (i != path[x]) {
|
||||
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v); wtype w = edge[i].w;
|
||||
if (t1 != t2) { tmp += w; fa[t1] = t2; cnt++; }
|
||||
if (cnt == n - 1) { if (tmp < ret) { ret = tmp; } break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret == INF) { return -1; } //不存在
|
||||
return ret;
|
||||
}
|
||||
//最小树形图
|
||||
//朱刘算法 O(VE)
|
||||
typedef int wtype;
|
||||
const int N = 1005;
|
||||
const int M = 40005;
|
||||
const wtype INF = 0x3f3f3f3f;
|
||||
struct Edge { int u, v; wtype w; } edge[M];
|
||||
int n, m, pre[N], id[N], vis[N];
|
||||
wtype g[N][N], in[N];
|
||||
wtype Zhuliu(int root) {
|
||||
wtype res = 0; int u, v;
|
||||
for (;;) {
|
||||
memset(in, 0x3f, sizeof(in));
|
||||
memset(id, -1, sizeof(id));
|
||||
memset(vis, -1, sizeof(vis));
|
||||
for (int i = 0; i < m; i++) {
|
||||
if (edge[i].u != edge[i].v && edge[i].w < in[edge[i].v]) {
|
||||
pre[edge[i].v] = edge[i].u; in[edge[i].v] = edge[i].w;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i != root && in[i] == INF) { return -1; } //不存在最小树形图
|
||||
}
|
||||
int tn = 0; in[root] = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res += in[i]; v = i;
|
||||
while (vis[v] != i && id[v] == -1 && v != root) {
|
||||
vis[v] = i; v = pre[v];
|
||||
}
|
||||
if (v != root && id[v] == -1) {
|
||||
for (int u = pre[v]; u != v ; u = pre[u]) { id[u] = tn; }
|
||||
id[v] = tn++;
|
||||
}
|
||||
}
|
||||
if (tn == 0) { break; } //没有有向环
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (id[i] == -1) { id[i] = tn++; }
|
||||
}
|
||||
for (int i = 0; i < m;) {
|
||||
v = edge[i].v; edge[i].u = id[edge[i].u]; edge[i].v = id[edge[i].v];
|
||||
if (edge[i].u != edge[i].v) { edge[i++].w -= in[v]; }
|
||||
else { swap(edge[i], edge[--m]); }
|
||||
}
|
||||
n = tn; root = id[root];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//POJ 3164
|
||||
int main() {
|
||||
int C = 0, T, u, v, w;
|
||||
scanf("%d", &T);
|
||||
while (++C <= T) {
|
||||
memset(g, 0x3f, sizeof(g));
|
||||
scanf("%d%d", &n, &m);
|
||||
while (m--) {
|
||||
scanf("%d%d%d", &u, &v, &w);
|
||||
if (u == v) { continue; }
|
||||
g[u][v] = min(g[u][v], w);
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
if (g[i][j] < INF) { edge[m].u = i; edge[m].v = j; edge[m++].w = g[i][j]; }
|
||||
}
|
||||
}
|
||||
wtype ans = Zhuliu(0);
|
||||
printf("Case #%d: ", C);
|
||||
if (ans == -1) { puts("Possums!"); }
|
||||
else { printf("%d\n", ans); }
|
||||
}
|
||||
}
|
||||
//曼哈顿距离最小生成树
|
||||
//Kruskal O(VlogV)
|
||||
const int INF = 0x3f3f3f3f;
|
||||
struct Point {
|
||||
int x, y, id;
|
||||
bool operator<(const Point &r)const { return x < r.x || (x == r.x && y < r.y); }
|
||||
} p[N];
|
||||
struct Edge { //有效边
|
||||
int u, v, w;
|
||||
bool operator<(const Edge &r)const { return w < r.w; }
|
||||
} edge[N << 2];
|
||||
struct BIT { //树状数组, 找y-x大于当前的, 但是y+x最小的
|
||||
int min_val, pos;
|
||||
void init() { min_val = INF; pos = -1; }
|
||||
} bit[N];
|
||||
int n, tot, fa[N];
|
||||
int a[N], b[N];
|
||||
void addedge(int u, int v, int w) { edge[tot].u = u; edge[tot].v = v; edge[tot++].w = w; }
|
||||
int findfa(int x) { return fa[x] == -1 ? x : fa[x] = findfa(fa[x]); }
|
||||
inline int cost(Point a, Point b) { return abs(a.x - b.x) + abs(a.y - b.y); }
|
||||
inline int lowbit(int x) { return x & (-x); }
|
||||
void update(int i, int val, int pos) {
|
||||
for (; i > 0; i -= lowbit(i)) {
|
||||
if (val < bit[i].min_val) { bit[i].min_val = val; bit[i].pos = pos; }
|
||||
}
|
||||
}
|
||||
int query(int i, int m) { //查询[i, m]的最小值位置
|
||||
int min_val = INF, pos = -1;
|
||||
for (; i <= m; i += lowbit(i)) {
|
||||
if (bit[i].min_val < min_val) { min_val = bit[i].min_val; pos = bit[i].pos; }
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
void MMST() {
|
||||
tot = 0;
|
||||
for (int d = 0; d < 4; d++) { //4种坐标变换
|
||||
if (d == 1 || d == 3) { for (int i = 0; i < n; i++) { swap(p[i].x, p[i].y); } }
|
||||
else if (d == 2) { for (int i = 0; i < n; i++) { p[i].x = -p[i].x; } }
|
||||
sort(p, p + n);
|
||||
for (int i = 0; i < n; i++) { a[i] = b[i] = p[i].y - p[i].x; }
|
||||
sort(b, b + n);
|
||||
int m = unique(b, b + n) - b;
|
||||
for (int i = 1; i <= m; i++) { bit[i].init(); }
|
||||
for (int i = n - 1 ; i >= 0; i--) {
|
||||
int pos = lower_bound(b, b + m, a[i]) - b + 1, ans = query(pos, m);
|
||||
if (ans != -1) { addedge(p[i].id, p[ans].id, cost(p[i], p[ans])); }
|
||||
update(pos, p[i].x + p[i].y, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
int Kruskal() {
|
||||
MMST();
|
||||
memset(fa, -1, sizeof(fa));
|
||||
sort(edge, edge + tot);
|
||||
int ret = 0;
|
||||
for (int i = 0, k = 0; i < tot; i++) {
|
||||
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v);
|
||||
if (t1 != t2) {
|
||||
fa[t1] = t2; ret += edge[i].w;
|
||||
if (++k == n - 1) { return ret; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//POJ3241 求曼哈顿最小生成树上第k大的边
|
||||
int Kruskal(int k) {
|
||||
MMST(n, p);
|
||||
memset(fa, -1, sizeof(fa));
|
||||
sort(edge, edge + tot);
|
||||
for (int i = 0; i < tot; i++) {
|
||||
int u = edge[i].u, v = edge[i].v, t1 = findfa(u), t2 = findfa(v);
|
||||
if (t1 != t2) {
|
||||
fa[t1] = t2;
|
||||
if (--k == 0) { return edge[i].w; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//生成树计数
|
||||
//Matrix-Tree定理(Kirchhoff 矩阵-树定理)
|
||||
//1、G的度数矩阵D[G]是一个n*n的矩阵, 并且满足: 当i ≠ j时,dij = 0; 当i = j时, dij等于vi的度数
|
||||
//2、G的邻接矩阵A[G]也是一个n*n的矩阵, 并且满足: 如果vi vj之间有边直接相连, 则aij = 1, 否则为0
|
||||
//我们定义G的Kirchhoff矩阵(也称为拉普拉斯算子)C[G]为C[G] = D[G] - A[G], 则Matrix-Tree定理可以
|
||||
//描述为: G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n - 1阶主子式的行列式的绝对值
|
||||
//所谓n - 1阶主子式, 就是对于r(1 ≤ r ≤ n), 将C[G]的第r行、第r列同时去掉后得到的新矩阵, 用Cr[G]表示。
|
308
.ACM-Templates/图论-网络流.cpp
Normal file
308
.ACM-Templates/图论-网络流.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
//最大流
|
||||
//SAP + 邻接矩阵 O(V^2*E) 点的编号默认从0开始
|
||||
typedef long long ftype;
|
||||
int n, dis[N], pre[N], cur[N], gap[N];
|
||||
ftype cap[N][N];
|
||||
ftype SAP(int src, int sink, int n) {
|
||||
memset(dis, 0, sizeof(dis));
|
||||
memset(cur, 0, sizeof(cur));
|
||||
memset(gap, 0, sizeof(gap));
|
||||
int u = src; ftype mxflow = 0, aug = -1; pre[src] = src; gap[0] = n;
|
||||
while (dis[src] < n) {
|
||||
loop:
|
||||
for (int v = cur[u]; v < n; ++v) {
|
||||
if (cap[u][v] > 0 && dis[u] == dis[v] + 1) {
|
||||
if (aug == -1 || aug > cap[u][v]) { aug = cap[u][v]; }
|
||||
pre[v] = u; u = cur[u] = v;
|
||||
if (v == sink) {
|
||||
for (u = pre[u]; v != src; v = u, u = pre[u]) {
|
||||
cap[u][v] -= aug; cap[v][u] += aug;
|
||||
}
|
||||
mxflow += aug; aug = -1;
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
}
|
||||
int mndis = n - 1;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (cap[u][v] > 0 && mndis > dis[v]) { cur[u] = v; mndis = dis[v]; }
|
||||
}
|
||||
if (--gap[dis[u]] == 0) { break; }
|
||||
dis[u] = mndis + 1; gap[dis[u]]++; u = pre[u];
|
||||
}
|
||||
return mxflow;
|
||||
}
|
||||
//ISAP + 邻接表 O(V^2*E)
|
||||
typedef long long ftype;
|
||||
const ftype INF = 0x3f3f3f3f;
|
||||
int head[N], to[M], nxt[M], tot, dis[N], pre[N], cur[N], gap[N];
|
||||
ftype cap[M];
|
||||
inline void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
inline void addedge(int x, int y, ftype w, ftype rw = 0) {
|
||||
to[tot] = y; cap[tot] = w; nxt[tot] = head[x]; head[x] = tot++;
|
||||
to[tot] = x; cap[tot] = rw; nxt[tot] = head[y]; head[y] = tot++;
|
||||
}
|
||||
ftype ISAP(int src, int sink, int n) {
|
||||
memset(dis, 0, sizeof(dis));
|
||||
memset(gap, 0, sizeof(gap));
|
||||
memcpy(cur, head, sizeof(head));
|
||||
int u = src, v; ftype mxflow = 0; pre[u] = -1; gap[0] = n;
|
||||
while (dis[src] < n) {
|
||||
if (u == sink) {
|
||||
ftype mndis = INF;
|
||||
for (int i = pre[u]; ~i; i = pre[to[i ^ 1]]) {
|
||||
if (mndis > cap[i]) { mndis = cap[i]; }
|
||||
}
|
||||
for (int i = pre[u]; ~i; i = pre[to[i ^ 1]]) {
|
||||
cap[i] -= mndis; cap[i ^ 1] += mndis;
|
||||
}
|
||||
u = src; mxflow += mndis;
|
||||
continue;
|
||||
}
|
||||
bool flag = false;
|
||||
for (int i = cur[u]; ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (cap[i] > 0 && dis[v] + 1 == dis[u]) {
|
||||
flag = true; cur[u] = pre[v] = i; break;
|
||||
}
|
||||
}
|
||||
if (flag) { u = v; continue; }
|
||||
int mndis = n;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
if (cap[i] > 0 && dis[to[i]] < mndis) { mndis = dis[to[i]]; cur[u] = i; }
|
||||
}
|
||||
if (--gap[dis[u]] == 0) { return mxflow; }
|
||||
dis[u] = mndis + 1; gap[dis[u]]++;
|
||||
if (u != src) { u = to[pre[u] ^ 1]; }
|
||||
}
|
||||
return mxflow;
|
||||
}
|
||||
//ISAP + bfs标号 + queue + 邻接表 O(V^2*E)
|
||||
typedef long long ftype;
|
||||
const ftype INF = 0x3f3f3f3f;
|
||||
int n, head[N], to[M], nxt[M], tot, dis[N], pre[N], cur[N], gap[N];
|
||||
ftype cap[M];
|
||||
inline void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
inline void addedge(int x, int y, ftype w, ftype rw = 0) {
|
||||
to[tot] = y; cap[tot] = w; nxt[tot] = head[x]; head[x] = tot++;
|
||||
to[tot] = x; cap[tot] = rw; nxt[tot] = head[y]; head[y] = tot++;
|
||||
}
|
||||
void bfs(int sink) {
|
||||
memset(dis, -1, sizeof(dis));
|
||||
memset(gap, 0, sizeof(gap));
|
||||
dis[sink] = 0; gap[0] = 1;
|
||||
queue<int> que; que.push(sink);
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int i = head[u], v; ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (~dis[v]) { continue; }
|
||||
dis[v] = dis[u] + 1; gap[dis[v]]++; que.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
ftype ISAP(int src, int sink, int n) {
|
||||
bfs(sink);
|
||||
memcpy(cur, head, sizeof(head));
|
||||
int u = pre[src] = src, v, i; ftype mxflow = 0;
|
||||
while (dis[sink] < n) {
|
||||
if (u == sink) {
|
||||
ftype mndis = INF; int inser;
|
||||
for (i = src; i != sink; i = to[cur[i]]) {
|
||||
if (mndis > cap[cur[i]]) { mndis = cap[cur[i]]; inser = i; }
|
||||
}
|
||||
for (i = src; i != sink; i = to[cur[i]]) {
|
||||
cap[cur[i]] -= mndis; cap[cur[i] ^ 1] += mndis;
|
||||
}
|
||||
mxflow += mndis; u = inser;
|
||||
}
|
||||
for (i = cur[u]; ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (dis[v] + 1 == dis[u] && cap[i] > 0) { break; }
|
||||
}
|
||||
if (~i) { cur[u] = i; pre[to[i]] = u; u = to[i]; }
|
||||
else {
|
||||
if (--gap[dis[u]] == 0) { break; }
|
||||
int mndis = n;
|
||||
for (i = head[u]; ~i; i = nxt[i]) {
|
||||
if (cap[i] > 0 && mndis > dis[to[i]]) { cur[u] = i; mndis = dis[to[i]]; }
|
||||
}
|
||||
dis[u] = mndis + 1; gap[dis[u]]++; u = pre[u];
|
||||
}
|
||||
}
|
||||
return mxflow;
|
||||
}
|
||||
//Dinic O(V^2*E)
|
||||
typedef long long ftype;
|
||||
const ftype INF = 0x3f3f3f3f;
|
||||
int head[N], to[M], nxt[M], tot, dis[N], cur[N], src, sink;
|
||||
ftype cap[M];
|
||||
inline void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
inline void addedge(int x, int y, ftype w, ftype rw = 0) {
|
||||
to[tot] = y; cap[tot] = w; nxt[tot] = head[x]; head[x] = tot++;
|
||||
to[tot] = x; cap[tot] = rw; nxt[tot] = head[y]; head[y] = tot++;
|
||||
}
|
||||
bool bfs() {
|
||||
memset(dis, 0, sizeof(dis)); dis[src] = 1;
|
||||
queue<int> que; que.push(src);
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
for (int i = head[u], v; ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (cap[i] > 0 && !dis[v]) { dis[v] = dis[u] + 1; que.push(v); }
|
||||
}
|
||||
}
|
||||
return dis[sink];
|
||||
}
|
||||
ftype dfs(int u, ftype delta) {
|
||||
if (u == sink || delta == 0) { return delta; }
|
||||
ftype ret = 0;
|
||||
for (int &i = cur[u], v; delta && ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (cap[i] > 0 && dis[v] == dis[u] + 1) {
|
||||
ftype aug = dfs(v, min(cap[i], delta));
|
||||
if (!aug) { continue; }
|
||||
cap[i] -= aug; cap[i ^ 1] += aug; delta -= aug; ret += aug;
|
||||
if (!delta) { break; }
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ftype Dinic() {
|
||||
ftype ret = 0;
|
||||
while (bfs()) {
|
||||
memcpy(cur, head, sizeof(head));
|
||||
ret += dfs(src, INF);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//HLPP Highest Label Preflow Push O(V^3)
|
||||
typedef long long ftype;
|
||||
struct Edge { int v; ftype c; } edge[M];
|
||||
vector<int> e[N];
|
||||
int n, m, src, sink, c[N << 1], d[N], done[N];
|
||||
ftype w[N];
|
||||
bool vis[N];
|
||||
void init(int _n, int _src, int _sink) {
|
||||
n = _n; m = 0; src = _src; sink = _sink;
|
||||
for (int i = 0; i <= n; ++i) { e[i].clear(); }
|
||||
}
|
||||
inline void addEdge(int x, int y, ftype w, ftype rw = 0) {
|
||||
edge[m].v = y; edge[m].c = w; e[x].push_back(m++);
|
||||
edge[m].v = x; edge[m].c = rw; e[y].push_back(m++);
|
||||
}
|
||||
void bfs() {
|
||||
memset(c, 0, sizeof(c));
|
||||
fill(d, d + n, n + 1);
|
||||
queue<int> que; que.push(sink);
|
||||
c[n + 1] = n - 1; d[src] = n; d[sink] = 0;
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
c[n + 1]++; c[d[u]]++;
|
||||
for (int i = 0, v; i < (int)e[u].size(); i++) {
|
||||
v = edge[e[u][i]].v; ftype c = edge[e[u][i] ^ 1].c;
|
||||
if (d[v] == n + 1 && c > 0) { d[v] = d[u] + 1; que.push(v); }
|
||||
}
|
||||
}
|
||||
}
|
||||
int HLPP(int n) {
|
||||
memset(w, 0, sizeof(w));
|
||||
memset(done, 0, sizeof(done));
|
||||
memset(vis, 0, sizeof(vis));
|
||||
bfs();
|
||||
int todo = -1;
|
||||
vector<queue<int>> que(n << 1);
|
||||
vis[src] = vis[sink] = true;
|
||||
for (int i = 0, v; i < (int)e[src].size(); i++) {
|
||||
Edge &arc = edge[e[src][i]], &cra = edge[e[src][i] ^ 1]; v = arc.v;
|
||||
w[v] += arc.c; cra.c += arc.c; arc.c = 0;
|
||||
if (!vis[v]) { vis[v] = true; que[d[v]].push(v); todo = max(todo, d[v]); }
|
||||
}
|
||||
while (todo >= 0) {
|
||||
if (que[todo].empty()) { todo--; continue; }
|
||||
int u = que[todo].front(); que[todo].pop();
|
||||
vis[u] = false;
|
||||
while (done[u] < (int)e[u].size()) {
|
||||
Edge &arc = edge[e[u][done[u]]]; int v = arc.v;
|
||||
if (d[u] == d[v] + 1 && arc.c > 0) {
|
||||
Edge &cra = edge[e[u][done[u]] ^ 1]; ftype f = min(w[u], arc.c);
|
||||
w[u] -= f; w[v] += f; arc.c -= f; cra.c += f;
|
||||
if (!vis[v]) { vis[v] = true; que[d[v]].push(v); }
|
||||
if (w[u] == 0) { break; }
|
||||
}
|
||||
done[u]++;
|
||||
}
|
||||
if (w[u] > 0) {
|
||||
int du = d[u];
|
||||
c[d[u]]--; d[u] = n << 1;
|
||||
for (int i = 0, v; i < (int)e[u].size(); i++) {
|
||||
Edge &arc = edge[e[u][i]]; v = arc.v;
|
||||
if (d[u] > d[v] + 1 && arc.c > 0) { d[u] = d[v] + 1; done[u] = i; }
|
||||
}
|
||||
c[d[u]]++;
|
||||
if (c[du] == 0) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (d[i] > du && d[i] < n + 1) { c[d[i]]--; c[n + 1]++; d[i] = n + 1; }
|
||||
}
|
||||
}
|
||||
vis[u] = true; que[d[u]].push(u); todo = d[u];
|
||||
}
|
||||
}
|
||||
return w[sink];
|
||||
}
|
||||
//有上下界的网络流
|
||||
//无源汇上下界最大流
|
||||
//增加超级源汇点, 边的容量变为原弧的上界减去下界的差, 记录每个点的入流下界和-出流下界和
|
||||
//当下界和大于0时连一条超级源点到这个点的边, 容量为这个下界和
|
||||
//当下界和小于0时连一条这个点到超级汇点的边, 容量为下界和的绝对值
|
||||
//求一遍最大流, 如果源的出度满流的话, 即存在最大流, 否则则不存在最大流
|
||||
//有源汇上下界最大流
|
||||
//从汇连一条容量INF的边到源, 将有源汇转化为无源汇
|
||||
//判断是否满流后, 去掉超级源汇及其边(head[]删除即可, 调用最大流算法时, 点的数量要包括超级源汇)
|
||||
//再跑一遍原源到原汇的最大流, 输出即可
|
||||
//有源汇上下界最小流
|
||||
//增加超级源汇点, 求一遍超级源到超级汇的最大流
|
||||
//从原汇点连一条容量INF的边到原源点, 再求一遍超级源到超级汇的最大流
|
||||
//当且仅当超级源的出度满流时有可行解, 解为原汇点到原源点的反向弧
|
||||
////最小费用最大流 MCMF O(V*E*f)
|
||||
//最小费用流: 若dis[sink]为负则继续增广
|
||||
//最小费用最大流: 若dis[sink]不为INF则继续增广
|
||||
//求最大费用只需取相反数, 结果取相反数即可
|
||||
typedef long long ftype;
|
||||
const ftype INF = 0x3f3f3f3f;
|
||||
int head[N], to[M], nxt[M], tot, cur[N];
|
||||
ftype cap[M], cost[M], flow[N], dis[N], mncost, mxflow;
|
||||
bool vis[N];
|
||||
inline void init() { tot = 0; memset(head, -1, sizeof(head)); }
|
||||
inline void addedge(int x, int y, ftype w, ftype c) {
|
||||
to[tot] = y; cap[tot] = w; cost[tot] = c; nxt[tot] = head[x]; head[x] = tot++;
|
||||
to[tot] = x; cap[tot] = 0; cost[tot] = -c; nxt[tot] = head[y]; head[y] = tot++;
|
||||
}
|
||||
bool SPFA(int src, int sink) {
|
||||
memset(dis, 0x3f, sizeof(dis));
|
||||
memset(vis, 0, sizeof(vis));
|
||||
dis[src] = 0; cur[src] = -1; flow[src] = INF;
|
||||
queue<int> que; que.push(src);
|
||||
while (!que.empty()) {
|
||||
int u = que.front(); que.pop();
|
||||
vis[u] = false;
|
||||
for (int i = head[u], v; ~i; i = nxt[i]) {
|
||||
v = to[i];
|
||||
if (cap[i] > 0 && dis[v] > dis[u] + cost[i]) {
|
||||
dis[v] = dis[u] + cost[i]; flow[v] = min(flow[u], cap[i]); cur[v] = i;
|
||||
if (!vis[v]) { vis[v] = true; que.push(v); }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dis[sink] == INF) { return false; }
|
||||
mxflow += flow[sink]; mncost += flow[sink] * dis[sink];
|
||||
for (int i = cur[sink]; ~i; i = cur[to[i ^ 1]]) {
|
||||
cap[i] -= flow[sink]; cap[i ^ 1] += flow[sink];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ftype MCMF(int src, int sink) {
|
||||
mxflow = mncost = 0;
|
||||
while (SPFA(src, sink));
|
||||
return mncost;
|
||||
}
|
198
.ACM-Templates/图论-连通性.cpp
Normal file
198
.ACM-Templates/图论-连通性.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
//图的割点、桥与双连通分量
|
||||
//[点连通度与边连通度]
|
||||
//在一个无向连通图中, 如果有一个顶点集合, 删除这个顶点集合,
|
||||
//以及这个集合中所有顶点相关联的边以后, 原图变成多个连通块, 就称这个点集为割点集合
|
||||
//一个图的点连通度的定义为, 最小割点集合中的顶点数
|
||||
//类似的, 如果有一个边集合, 删除这个边集合以后, 原图变成多个连通块, 就称这个点集为割边集合
|
||||
//一个图的边连通度的定义为, 最小割边集合中的边数
|
||||
//[双连通图、割点与桥]
|
||||
//如果一个无向连通图的点连通度大于1, 则称该图是点双连通的(point biconnected), 简称双连通或重连通
|
||||
//一个图有割点, 当且仅当这个图的点连通度为1, 则割点集合的唯一元素被称为割点(cut point), 又叫关节点(articulation point)
|
||||
//如果一个无向连通图的边连通度大于1, 则称该图是边双连通的(edge biconnected), 简称双连通或重连通
|
||||
//一个图有桥, 当且仅当这个图的边连通度为1, 则割边集合的唯一元素被称为桥(bridge), 又叫关节边(articulation edge)
|
||||
//可以看出, 点双连通与边双连通都可以简称为双连通, 它们之间是有着某种联系的, 下文中提到的双连通, 均既可指点双连通, 又可指边双连通
|
||||
//[双连通分量]
|
||||
//在图G的所有子图G'中, 如果G'是双连通的, 则称G'为双连通子图
|
||||
//如果一个双连通子图G'它不是任何一个双连通子图的真子集, 则G'为极大双连通子图
|
||||
//双连通分量(biconnected component), 或重连通分量, 就是图的极大双连通子图。特殊的, 点双连通分量又叫做块
|
||||
//[求割点与桥]
|
||||
//对图深度优先搜索, 定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号
|
||||
//定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点, 即DFS序号最小的节点
|
||||
//根据定义, 则有: Low(u) = Min {DFS(u) DFS(v) (u,v)为后向边(返祖边) 等价于DFS(v) < DFS(u)且v不为u的父亲节点Low(v) (u,v)为树枝边(父子边)}
|
||||
//一个顶点u是割点, 当且仅当满足(1)或(2)
|
||||
//(1)u为树根, 且u有多于一个子树
|
||||
//(2)u不为树根, 且满足存在(u,v)为树枝边(或称父子边, 即u为v在搜索树中的父亲), 使得DFS(u)<=Low(v)
|
||||
//一条无向边(u,v)是桥, 当且仅当(u,v)为树枝边, 且满足DFS(u)<Low(v)
|
||||
//[求双连通分量]
|
||||
//对于点双连通分量, 实际上在求割点的过程中就能顺便把每个点双连通分量求出
|
||||
//建立一个栈, 存储当前双连通分量, 在搜索图时, 每找到一条树枝边或后向边(非横叉边), 就把这条边加入栈中
|
||||
//如果遇到某时满足DFS(u)<=Low(v), 说明u是一个割点, 同时把边从栈顶一个个取出,
|
||||
//直到遇到了边(u,v), 取出的这些边与其关联的点, 组成一个点双连通分量
|
||||
//割点可以属于多个点双连通分量, 其余点和每条边只属于且属于一个点双连通分量
|
||||
//对于边双连通分量, 只需在求出所有的桥以后, 把桥边删除, 原图变成了多个连通块, 则每个连通块就是一个边双连通分量
|
||||
//桥不属于任何一个边双连通分量, 其余的边和每个顶点都属于且只属于一个边双连通分量
|
||||
//[构造双连通图]
|
||||
//方法为首先求出所有的桥, 然后删除这些桥边, 剩下的每个连通块都是一个双连通子图
|
||||
//把每个双连通子图收缩为一个顶点, 再把桥边加回来, 最后的这个图一定是一棵树, 边连通度为1
|
||||
//统计出树中度为1的节点的个数, 即为叶节点的个数, 记为leaf
|
||||
//则至少在树上添加(leaf + 1) / 2条边, 就能使树达到边二连通, 所以至少添加的边数就是(leaf + 1) / 2
|
||||
//具体方法为, 首先把两个最近公共祖先最远的两个叶节点之间连接一条边, 这样可以把这两个点到祖先的路径上所有点收缩到一起,
|
||||
//因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点, 这样一对一对找完, 恰好是(leaf + 1) / 2次, 把所有点收缩到了一起
|
||||
|
||||
//有向图的强连通分量
|
||||
//Kosaraju算法 O(V + E)
|
||||
vector<int> e[N], re[N], pos;
|
||||
bool vis[N];
|
||||
int belong[N], num[N], scc, cnt; //所属分量, 分量点数, 分量个数
|
||||
void dfs(int u) {
|
||||
vis[u] = true;
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
if (!vis[e[u][i]]) { dfs(e[u][i]); }
|
||||
}
|
||||
pos.push_back(u);
|
||||
}
|
||||
void rdfs(int u, int k) {
|
||||
vis[u] = true; belong[u] = k; cnt++;
|
||||
for (int i = 0; i < (int)re[u].size(); i++) {
|
||||
if (!vis[re[u][i]]) { rdfs(re[u][i], k); }
|
||||
}
|
||||
}
|
||||
void SCC(int n) {
|
||||
memset(vis, 0, sizeof(vis)); pos.clear();
|
||||
for (int i = 0; i < n; i++) { if (!vis[i]) { dfs(i); } }
|
||||
memset(vis, 0, sizeof(vis)); memset(num, 0, sizeof(num)); scc = 0;
|
||||
for (int i = (int)pos.size() - 1; i >= 0; i--) {
|
||||
if (!vis[pos[i]]) { cnt = 0; rdfs(pos[i], ++scc); num[scc] = cnt; }
|
||||
}
|
||||
}
|
||||
//Tarjan算法 O(V + E)
|
||||
vector<int> e[N];
|
||||
int low[N], dfn[N], index, belong[N], num[N], scc; //所属分量, 分量点数, 分量个数
|
||||
stack<int> stk;
|
||||
bool instack[N];
|
||||
void Tarjan(int u) {
|
||||
int v; low[u] = dfn[u] = ++index; stk.push(u); instack[u] = true;
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
int v = e[u][i];
|
||||
if (!dfn[v]) { Tarjan(v); low[u] = min(low[u], low[v]); }
|
||||
else if (instack[v] && low[u] > dfn[v]) { low[u] = dfn[v]; }
|
||||
}
|
||||
if (low[u] == dfn[u]) {
|
||||
scc++;
|
||||
do {
|
||||
v = stk.top(); stk.pop();
|
||||
instack[v] = false; belong[v] = scc; num[scc]++;
|
||||
} while (v != u);
|
||||
}
|
||||
}
|
||||
void SCC(int n) {
|
||||
memset(dfn, 0, sizeof(dfn)); memset(num, 0, sizeof(num));
|
||||
memset(instack, 0, sizeof(instack)); index = scc = 0;
|
||||
while (!stk.empty()) { stk.pop(); }
|
||||
for (int i = 0; i < n; i++) { if (!dfn[i]) { Tarjan(i); } }
|
||||
}
|
||||
//无向图的割点和桥
|
||||
//Tarjan算法 O(V + E)
|
||||
int low[N], dfn[N], index, addblock[N], bridge;
|
||||
bool instack[N], cut[N], ecut[M];
|
||||
stack<int> stk;
|
||||
void Tarjan(int u, int p) {
|
||||
int son = 0; low[u] = dfn[u] = ++index;
|
||||
stk.push(u); instack[u] = true;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (v == p) { continue; }
|
||||
if (!dfn[v]) {
|
||||
Tarjan(v, u); son++;
|
||||
low[u] = min(low[u], low[v]);
|
||||
if (u != p && low[v] >= dfn[u]) { //割点
|
||||
cut[u] = true; addblock[u]++;
|
||||
}
|
||||
if (low[v] > dfn[u]) { //割边
|
||||
bridge++; ecut[i] = ecut[i ^ 1] = true;
|
||||
}
|
||||
} else if (low[u] > dfn[v]) { low[u] = dfn[v]; }
|
||||
}
|
||||
if (u == p && son > 1) { cut[u] = true; }
|
||||
if (u == p) { addblock[u] = son - 1; }
|
||||
instack[u] = false; stk.pop();
|
||||
}
|
||||
void CUT(int n) {
|
||||
memset(dfn, 0, sizeof(dfn)); memset(addblock, 0, sizeof(addblock));
|
||||
memset(instack, 0, sizeof(instack)); memset(cut, 0, sizeof(cut)); memset(ecut, 0, sizeof(ecut));
|
||||
while (!stk.empty()) { stk.pop(); } index = bridge = 0;
|
||||
for (int i = 0; i < n; i++) { if (!dfn[i]) { Tarjan(i, i); } }
|
||||
}
|
||||
//无向图的边双连通分量
|
||||
//求出所有的桥以后, 把桥边删除, 原图变成了多个连通块, 则每个连通块就是一个边双连通分量
|
||||
//桥不属于任何一个边双连通分量, 其余的边和每个顶点都属于且只属于一个边双连通分量
|
||||
//Tarjan算法 O(V + E)
|
||||
int low[N], dfn[N], index, belong[N], bridge, block;
|
||||
bool instack[N], ecut[M];
|
||||
stack<int> stk;
|
||||
void Tarjan(int u, int p) {
|
||||
low[u] = dfn[u] = ++index; stk.push(u); instack[u] = true;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (v == p) { continue; }
|
||||
if (!dfn[v]) {
|
||||
Tarjan(v, u);
|
||||
low[u] = min(low[u], low[v]);
|
||||
if (low[v] > dfn[u]) { //割边
|
||||
bridge++; ecut[i] = ecut[i ^ 1] = true;
|
||||
}
|
||||
} else if (low[u] > dfn[v]) { low[u] = dfn[v]; }
|
||||
}
|
||||
if (low[u] == dfn[u]) {
|
||||
int v; block++;
|
||||
do {
|
||||
v = stk.top(); stk.pop(); instack[v] = false; belong[v] = block;
|
||||
} while (u != v);
|
||||
}
|
||||
}
|
||||
void EBCC(int n) {
|
||||
memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack)); memset(cut, 0, sizeof(cut));
|
||||
while (!stk.empty()) { stk.pop(); } index = bridge = block = 0;
|
||||
for (int i = 0; i < n; i++) { if (!dfn[i]) { Tarjan(i, -1); } }
|
||||
}
|
||||
//无向图的点双连通分量
|
||||
//对于点双连通分量, 实际上在求割点的过程中就能顺便把每个点双连通分量求出
|
||||
//建立一个栈, 存储当前双连通分量, 在搜索图时, 每找到一条树枝边或后向边(非横叉边), 就把这条边加入栈中
|
||||
//如果遇到某时满足DFS(u)<=Low(v), 说明u是一个割点, 同时把边从栈顶一个个取出,
|
||||
//直到遇到了边(u,v), 取出的这些边与其关联的点, 组成一个点双连通分量
|
||||
//割点可以属于多个点双连通分量, 其余点和每条边只属于且属于一个点双连通分量
|
||||
//Tarjan算法 O(V + E)
|
||||
int low[N], dfn[N], index, belong[N], block; //所属分量, 分量点数, 分量个数
|
||||
stack<int> stk;
|
||||
bool instack[N];
|
||||
void Tarjan(int u, int p) {
|
||||
low[u] = dfn[u] = ++index;
|
||||
stk.push(u); instack[u] = true;
|
||||
for (int i = head[u]; ~i; i = nxt[i]) {
|
||||
int v = to[i];
|
||||
if (v == p) { continue; }
|
||||
if (!dfn[v]) {
|
||||
Tarjan(v, u);
|
||||
low[u] = min(low[u], low[v]);
|
||||
if (low[v] >= dfn[u]) { //u为割点
|
||||
int vv; block++;
|
||||
do {
|
||||
vv = stk.top(); stk.pop(); instack[vv] = false;
|
||||
belong[vv] = block;
|
||||
} while (vv != v);
|
||||
}
|
||||
} else if (instack[v] && low[u] > dfn[v]) { low[u] = dfn[v]; }
|
||||
}
|
||||
}
|
||||
void BCC(int n) {
|
||||
memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack));
|
||||
while (!stk.empty()) { stk.pop(); } index = block = 0;
|
||||
for (int i = 0; i < n; i++) { if (!dfn[i]) { Tarjan(i, -1); } }
|
||||
}
|
||||
//构造双连通图
|
||||
//方法为首先求出所有的桥, 然后删除这些桥边, 剩下的每个连通块都是一个双连通子图
|
||||
//把每个双连通子图收缩为一个顶点, 再把桥边加回来, 最后的这个图一定是一棵树, 边连通度为1
|
||||
//统计出树中度为1的节点的个数, 即为叶节点的个数, 记为leaf
|
||||
//则至少在树上添加(leaf + 1) / 2条边, 就能使树达到边二连通, 所以至少添加的边数就是(leaf + 1) / 2
|
||||
//具体方法为, 首先把两个最近公共祖先最远的两个叶节点之间连接一条边, 这样可以把这两个点到祖先的路径上所有点收缩到一起,
|
||||
//因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点, 这样一对一对找完, 恰好是(leaf + 1) / 2次, 把所有点收缩到了一起
|
401
.ACM-Templates/字符串.cpp
Normal file
401
.ACM-Templates/字符串.cpp
Normal file
|
@ -0,0 +1,401 @@
|
|||
//hash_fun.h
|
||||
inline size_t __stl_hash_string(const char *s) {
|
||||
size_t h = 0;
|
||||
for (; *s; ++s) { h = 5 * h + *s; }
|
||||
return h;
|
||||
}
|
||||
//for hash_map<string, XXX>
|
||||
struct str_hash {
|
||||
size_t operator()(const string &str)const {
|
||||
return __stl_hash_string(str.c_str());
|
||||
}
|
||||
};
|
||||
//BKDR Hash Function
|
||||
inline size_t BKDRHash(const char *str) {
|
||||
size_t h = 0, seed = 131; //31 131 1313 13131 131313 etc..
|
||||
while (*str) { h = h * seed + (*str++); }
|
||||
return h & 0x7FFFFFFF;
|
||||
}
|
||||
//字符串hash
|
||||
const int N = 20005, P = 31, D = 1000173169;
|
||||
int n, pp[N] = {1}, hs[N]; char s[N];
|
||||
int get(int l, int r) { return ((hs[r] - (ll)hs[l - 1] * pp[r - l + 1]) % D + D) % D; }
|
||||
int main() {
|
||||
scanf("%d%s", &n, s + 1);
|
||||
for (int i = 1; i <= n; i++) { pp[i] = pp[i - 1] * P % D; }
|
||||
for (int i = 1; i <= n; i++) { hs[i] = ((ll)hs[i - 1] * P + s[i]) % D; }
|
||||
}
|
||||
//手写hash_map
|
||||
const int P = 13131;
|
||||
char key[N][M];
|
||||
typedef struct Node { int id, val; } etype;
|
||||
template<size_t(*Hash)(const char *)> struct hashmap {
|
||||
vector<etype> hs[P];
|
||||
void init() { for (int i = 0; i < P; i++) { hs[i].clear(); } }
|
||||
void insert(int id, int val) {
|
||||
int h = Hash(key[id]) % P; hs[h].push_back((etype) {id, val});
|
||||
}
|
||||
bool erase(char *buf) {
|
||||
int h = Hash(buf) % P;
|
||||
for (size_t i = 0; i < n; i++) { if (!strcmp(buf, key[hs[h][i].id])) { hs[h].erase(hs[h].begin() + i); return true; } }
|
||||
return false;
|
||||
}
|
||||
int query(char *buf) {
|
||||
int h = Hash(buf) % P;
|
||||
for (size_t i = 0; i < n; i++) { if (!strcmp(buf, key[hs[h][i].id])) { return hs[h][i].val; } }
|
||||
return false;
|
||||
}
|
||||
};
|
||||
hashmap<BKDRHash> mp;
|
||||
//Manacher 最长回文子串
|
||||
//最长回文子串对应原串T中的位置: l = (i - R[i]) / 2; r = (i + R[i]) / 2 - 2;
|
||||
char s[N], tmp[N << 1];
|
||||
int dp[N << 1];
|
||||
void Manacher(char *s, int len) {
|
||||
int l = 0, mx = 0, id = 0; tmp[l++] = '$'; tmp[l++] = '#';
|
||||
for (int i = 0; i < len; i++) { tmp[l++] = s[i]; tmp[l++] = '#'; }
|
||||
tmp[l] = 0;
|
||||
for (int i = 0; i < l; i++) {
|
||||
dp[i] = mx > i ? min(dp[(id << 1) - i], mx - i) : 1;
|
||||
while (tmp[i + dp[i]] == tmp[i - dp[i]]) { dp[i]++; }
|
||||
if (i + dp[i] > mx) { mx = i + dp[i]; id = i; }
|
||||
}
|
||||
}
|
||||
int main() {
|
||||
while (~scanf("%s", s)) {
|
||||
int len = strlen(s), mlen = (len << 1) + 2, mxlen = 0, mxpos = 0;
|
||||
Manacher(s, len);
|
||||
for (int i = 0; i < mlen; i++) {
|
||||
if (mxlen < dp[i]) { mxlen = dp[i]; mxpos = i; }
|
||||
}
|
||||
printf("%d\n", mxlen - 1); //s.substr((mxpos - mxlen) >> 1, mxlen - 1);
|
||||
}
|
||||
}
|
||||
//字符串最小表示
|
||||
int minString(char *s) {
|
||||
int m = strlen(s), i, j, k;
|
||||
char ss[m << 1]; strcpy(ss, s); strcpy(ss + m, s);
|
||||
for (i = k = 0, j = 1; k < m && i < m && j < m;) {
|
||||
for (k = 0; k < m && ss[i + k] == ss[j + k]; k++);
|
||||
if (k < m) {
|
||||
if (ss[i + k] > ss[j + k]) { i += k + 1; } //最大则改为<
|
||||
else { j += k + 1; }
|
||||
if (i == j) { j++; }
|
||||
}
|
||||
}
|
||||
return min(i, j);
|
||||
}
|
||||
//strstr 在str1中查找str2的第一次出现 无则返回NULL
|
||||
char *strstr(const char *str1, const char *str2);
|
||||
//KMP O(M + N)
|
||||
//nxt[]的含义:x[i-nxt[i]...i-1]=x[0...nxt[i]-1]
|
||||
//nxt[i]为满足x[i-z...i-1]=x[0...z-1]的最大z值(就是x的自身匹配)
|
||||
char x[N], y[N];
|
||||
int nxt[N];
|
||||
void getnxt(char *x, int m, int nxt[]) {
|
||||
int i = 0, j = -1; nxt[0] = -1;
|
||||
while (i < m) {
|
||||
while (j != -1 && x[i] != x[j]) { j = nxt[j]; }
|
||||
nxt[++i] = ++j;
|
||||
}
|
||||
}
|
||||
//改进版
|
||||
void getnxt(char *x, int m, int nxt[]) {
|
||||
int i = 0, j = -1; nxt[0] = -1;
|
||||
while (i < m) {
|
||||
while (j != -1 && x[i] != x[j]) { j = nxt[j]; }
|
||||
if (x[++i] == x[++j]) { nxt[i] = nxt[j]; }
|
||||
else { nxt[i] = j; }
|
||||
}
|
||||
}
|
||||
//返回x在y中出现的次数, 可以重叠
|
||||
//x是模式串, y是主串
|
||||
int KMPCount(char *x, int m, char *y, int n, int nxt[]/*, int &longest, int &lp*/) {
|
||||
int i = 0, j = 0, ans = 0; //longest = 0; lp = 0;
|
||||
while (i < n) {
|
||||
while (j != -1 && y[i] != x[j]) { j = nxt[j]; }
|
||||
i++; j++;
|
||||
//if (j > longest) { longest = j; lp = i - j; }
|
||||
if (j >= m) { j = nxt[j]; ans++; }
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
//扩展KMP
|
||||
//nxt[i]:x[i...m-1]与x[0...m-1]的最长公共前缀
|
||||
//ext[i]:y[i...n-1]与x[0...m-1]的最长公共前缀
|
||||
int nxt[N], ext[N];
|
||||
void getnxt(char *x, int m, int nxt[]) {
|
||||
int i = 2, j = 0, k = 1;
|
||||
while (j + 1 < m && x[j] == x[j + 1]) { j++; }
|
||||
nxt[0] = m; nxt[1] = j;
|
||||
for (; i < m; i++) {
|
||||
int p = nxt[k] + k - 1, l = nxt[i - k];
|
||||
if (i + l < p + 1) { nxt[i] = l; }
|
||||
else {
|
||||
j = max(0, p - i + 1);
|
||||
while (i + j < m && x[i + j] == x[j]) { j++; }
|
||||
nxt[i] = j; k = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
void getext(char *x, int m, char *y, int n, int nxt[], int ext[]) {
|
||||
getnxt(x, m);
|
||||
int i = 1, j = 0, k = 0;
|
||||
while (j < n && j < m && x[j] == y[j]) { j++; }
|
||||
ext[0] = j;
|
||||
for (; i < n; i++) {
|
||||
int p = ext[k] + k - 1, l = nxt[i - k];
|
||||
if (i + l < p + 1) { ext[i] = l; }
|
||||
else {
|
||||
j = max(0, p - i + 1);
|
||||
while (i + j < n && j < m && y[i + j] == x[j]) { j++; }
|
||||
ext[i] = j; k = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Sunday
|
||||
int Sunday(char *x, int m, char *y, int n) {
|
||||
int nxt[26] = {0};
|
||||
for (int j = 0; j < 26; j++) { nxt[j] = m + 1; }
|
||||
for (int j = 0; j < m; j++) { nxt[x[j] - 'a'] = m - j; }
|
||||
for (int pos = 0, i, j; pos <= n - m;) {
|
||||
for (i = pos, j = 0; j < m; i++, j++) {
|
||||
if (y[i] != x[j]) { pos += nxt[y[pos + m] - 'a']; break; }
|
||||
}
|
||||
if (j == m) { return pos; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//Rabin-Karp
|
||||
#define UNSIGNED(x) ((unsigned char)x)
|
||||
const int d = 257;
|
||||
int hashMatch(char *s, int m, char *p, int n) {
|
||||
if (m > n || m == 0 || n == 0) { return -1; }
|
||||
//sv为s子串的hash结果, pv为p的hash结果, base为d的m-1次方
|
||||
unsigned sv = UNSIGNED(s[0]), pv = UNSIGNED(p[0]), base = 1;
|
||||
int i, j;
|
||||
//初始化sv, pv, base
|
||||
for (i = 1; i < m; i++) {
|
||||
pv = pv * d + UNSIGNED(p[i]);
|
||||
sv = sv * d + UNSIGNED(s[i]);
|
||||
base *= d;
|
||||
}
|
||||
i = m - 1;
|
||||
do {
|
||||
if (!(sv ^ pv)) {
|
||||
for (j = 0; j < m && s[i - m + 1 + j] == p[j]; j++);
|
||||
if (j == m) { return i - m + 1; }
|
||||
}
|
||||
if (++i >= n) { break; }
|
||||
//O(1)时间内更新sv, sv + UNSIGNED(s[i - m]) * (~base + 1)等价于sv - UNSIGNED(s[i - m]) * base
|
||||
sv = (sv + UNSIGNED(s[i - m]) * (~base + 1)) * d + UNSIGNED(s[i]);
|
||||
} while (i < n);
|
||||
return -1;
|
||||
}
|
||||
//Trie
|
||||
//数组实现
|
||||
struct Trie {
|
||||
int nxt[N * 20][26], val[N * 20], root, tot;
|
||||
void init() { memset(nxt, 0, sizeof(nxt)); memset(val, 0, sizeof(val)); root = tot = 1; }
|
||||
void insert(char *buf, int id) {
|
||||
int len = strlen(buf), now = root;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
if (!nxt[now][c = buf[i] - 'a']) { nxt[now][c] = ++tot; }
|
||||
now = nxt[now][c];
|
||||
}
|
||||
val[now] = id;
|
||||
}
|
||||
int query(char *buf) {
|
||||
int len = strlen(buf), now = root;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
if (!nxt[now][c = buf[i] - 'a']) { return -1; }
|
||||
now = nxt[now][c];
|
||||
}
|
||||
return val[now];
|
||||
}
|
||||
} tr;
|
||||
//指针实现
|
||||
struct Node { Node *nxt[26]; int val; };
|
||||
struct Trie {
|
||||
Node *root;
|
||||
void init() { erase(root); root = new Node(); }
|
||||
void insert(char *buf, int id) {
|
||||
int len = strlen(buf); Node *now = root;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
if (!now->nxt[c = buf[i] - 'a']) { now->nxt[c] = new Node(); }
|
||||
now = now->nxt[c];
|
||||
}
|
||||
now->val = id;
|
||||
}
|
||||
void erase(Node *p) {
|
||||
if (p) { for (int i = 0; i < 26; i++) { erase(p->nxt[i]); } delete p; }
|
||||
}
|
||||
int query(char *buf) {
|
||||
int len = strlen(buf); Node *now = root;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
if (!now->nxt[c = buf[i] - 'a']) { return -1; }
|
||||
now = now->nxt[c];
|
||||
}
|
||||
return now->val;
|
||||
}
|
||||
} tr;
|
||||
//AC自动机
|
||||
struct AC {
|
||||
int nxt[N * 20][26], fail[N * 20], val[N * 20], root, tot;
|
||||
void init() { memset(nxt, 0, sizeof(nxt)); memset(val, 0, sizeof(val)); root = tot = 1; }
|
||||
void insert(char *buf, int id) {
|
||||
int len = strlen(buf), now = root;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
if (!nxt[now][c = buf[i] - 'a']) { nxt[now][c] = ++tot; }
|
||||
now = nxt[now][c];
|
||||
}
|
||||
val[now] = id;
|
||||
}
|
||||
void build() {
|
||||
queue<int> que; fail[root] = root;
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (!nxt[root][i]) { nxt[root][i] = root; }
|
||||
else { fail[nxt[root][i]] = root; que.push(nxt[root][i]); }
|
||||
}
|
||||
while (!que.empty()) {
|
||||
int now = que.front(); que.pop();
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (!nxt[now][i]) { nxt[now][i] = nxt[fail[now]][i]; }
|
||||
else { fail[nxt[now][i]] = nxt[fail[now]][i]; que.push(nxt[now][i]); }
|
||||
}
|
||||
}
|
||||
}
|
||||
int query(char *buf) {
|
||||
int len = strlen(buf), now = root, res = 0;
|
||||
for (int i = 0, c; i < len; i++) {
|
||||
for (int tmp = now = nxt[now][c = buf[i] - 'a']; tmp != root; tmp = fail[tmp]) {
|
||||
res += val[tmp]; //val[tmp] = 0;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
} ac;
|
||||
//后缀数组
|
||||
//n:串长
|
||||
//m:字符集大小
|
||||
//s[0..n - 1]:字符串
|
||||
//sa[1..n]:字典序第 i 小的是哪个后缀
|
||||
//rnk[0..n - 1]:后缀 i 的排名
|
||||
//height[i]:lcp(sa[i], sa[i - 1])
|
||||
int rnk[N], sa[N], height[N], tmp[N], cnt[N];
|
||||
void SA(char *s, int n, int m) {
|
||||
int i, j, k; n++;
|
||||
memset(rnk, 0, sizeof(rnk)); memset(sa, 0, sizeof(sa)); memset(height, 0, sizeof(height));
|
||||
memset(tmp, 0, sizeof(tmp)); memset(cnt, 0, sizeof(cnt));
|
||||
for (i = 0; i < n; i++) { cnt[rnk[i] = s[i]]++; }
|
||||
for (i = 1; i < m; i++) { cnt[i] += cnt[i - 1]; }
|
||||
for (i = 0; i < n; i++) { sa[--cnt[rnk[i]]] = i; }
|
||||
for (k = 1; k <= n; k <<= 1) {
|
||||
for (i = 0; i < n; i++) {
|
||||
j = sa[i] - k;
|
||||
if (j < 0) { j += n; }
|
||||
tmp[cnt[rnk[j]]++] = j;
|
||||
}
|
||||
sa[tmp[cnt[0] = 0]] = j = 0;
|
||||
for (i = 1; i < n; i++) {
|
||||
if (rnk[tmp[i]] != rnk[tmp[i - 1]] || rnk[tmp[i] + k] != rnk[tmp[i - 1] + k]) { cnt[++j] = i; }
|
||||
sa[tmp[i]] = j;
|
||||
}
|
||||
memcpy(rnk, sa, n * sizeof(int));
|
||||
memcpy(sa, tmp, n * sizeof(int));
|
||||
if (j >= n - 1) { break; }
|
||||
}
|
||||
for (j = rnk[height[i = k = 0] = 0]; i < n - 1; i++, k++) {
|
||||
while (k >= 0 && s[i] != s[sa[j - 1] + k]) { height[j] = k--, j = rnk[sa[j] + 1]; }
|
||||
}
|
||||
}
|
||||
//后缀自动机
|
||||
const int N = 1000005;
|
||||
const int N_CHAR = 26;
|
||||
struct SuffixAutomaton {
|
||||
struct Node { Node *fail, *next[N_CHAR]; int val, right; };
|
||||
Node mempool[N << 1]; int n_node;
|
||||
Node *new_node(int v) {
|
||||
Node *p = &mempool[n_node++]; memset(p->next, 0, sizeof(p->next));
|
||||
p->fail = 0; p->right = 0; p->val = v; return p;
|
||||
}
|
||||
Node *root, *last;
|
||||
SuffixAutomaton() { clear(); }
|
||||
void clear() { root = last = new_node(n_node = 0); }
|
||||
void add(int c) {
|
||||
Node *p = last, *np = new_node(p->val + 1);
|
||||
while (p && !p->next[c]) { p->next[c] = np; p = p->fail; }
|
||||
if (!p) { np->fail = root; }
|
||||
else {
|
||||
Node *q = p->next[c];
|
||||
if (p->val + 1 == q->val) { np->fail = q; }
|
||||
else {
|
||||
Node *nq = new_node(p->val + 1);
|
||||
for (int i = 0; i < N_CHAR; i++) { nq->next[i] = q->next[i]; }
|
||||
nq->fail = q->fail; q->fail = np->fail = nq;
|
||||
while (p && p->next[c] == q) { p->next[c] = nq; p = p->fail; }
|
||||
}
|
||||
}
|
||||
last = np; np->right = 1;
|
||||
}
|
||||
Node *go(const char *s) {
|
||||
Node *p = root; int cL = 0; //与s匹配的长度
|
||||
for (int i = 0; s[i]; i++) {
|
||||
int c = s[i] - 'a';
|
||||
if (p->next[c]) { p = p->next[c], ++cL; }
|
||||
else {
|
||||
while (p && !p->next[c]) { p = p->fail; }
|
||||
if (!p) { cL = 0; p = root; }
|
||||
else { cL = p->val + 1; p = p->next[c]; }
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
int d[N << 1]; Node *b[N << 1];
|
||||
void toposort() {
|
||||
for (int i = 0; i <= n_node; i++) { d[i] = 0; }
|
||||
int mx_val = 0;
|
||||
for (int i = 0; i < n_node; i++) { mx_val = max(mx_val, mempool[i].val); d[mempool[i].val]++; }
|
||||
for (int i = 1; i <= mx_val; i++) { d[i] += d[i - 1]; }
|
||||
for (int i = 0; i < n_node; i++) { b[--d[mempool[i].val]] = &mempool[i]; }
|
||||
}
|
||||
void updateright() {
|
||||
toposort();
|
||||
for (int i = n_node - 1; i; i--) { b[i]->fail->right += b[i]->right; }
|
||||
}
|
||||
} sa;
|
||||
//回文树
|
||||
struct PalindromicTree {
|
||||
int nxt[N][26]; //指向的串为当前串两端加上同一个字符构成
|
||||
int fail[N]; //表示失配后跳转到长度小于该串且以该节点表示回文串的最后一个字符结尾的最长回文串表示的节点
|
||||
int cnt[N]; //表示节点表示的本质不同的串的个数(建树时求出的不是完全的, 最后count函数跑一遍以后才是正确的)
|
||||
int num[N]; //表示以节点表示的最长回文串的最右端点为回文串结尾的回文串个数
|
||||
int len[N]; //表示节点表示的回文串长度
|
||||
int S[N]; //表示第i次添加的字符(S[0] = -1(任意一个在串中不会出现的字符))
|
||||
int last; //指向新添加一个字母后所形成的最长回文串表示的节点
|
||||
int n; //表示添加的字符个数
|
||||
int tot; //表示节点个数
|
||||
int newnode(int l) { len[tot] = l; return tot++; }
|
||||
void init() {
|
||||
memset(nxt, 0, sizeof(nxt)); memset(cnt, 0, sizeof(cnt)); memset(len, 0, sizeof(len));
|
||||
newnode(0); newnode(-1); tot = last = n = 0; S[n] = -1; fail[0] = 1;
|
||||
}
|
||||
int getfail(int x) { //失配后找一个尽量最长的
|
||||
while (S[n - len[x] - 1] != S[n]) { x = fail[x]; }
|
||||
return x;
|
||||
}
|
||||
void add(int c) {
|
||||
c -= 'a'; S[++n] = c;
|
||||
int cur = getfail(last); //通过上一个回文串找这个回文串的匹配位置
|
||||
if (!nxt[cur][c]) { //如果这个回文串没有出现过, 说明出现了一个新的本质不同的回文串
|
||||
int now = newnode(len[cur] + 2); //新建节点
|
||||
fail[now] = nxt[getfail(fail[cur])][c]; //和AC自动机一样建立fail指针, 以便失配后跳转
|
||||
nxt[cur][c] = now; num[now] = num[fail[now]] + 1;
|
||||
}
|
||||
cnt[last = nxt[cur][c]]++;
|
||||
}
|
||||
void count() {
|
||||
for (int i = tot - 1; i >= 0; i--) { cnt[fail[i]] += cnt[i]; }
|
||||
//父亲累加儿子的cnt, 因为如果fail[v] = u, 则u一定是v的子回文串
|
||||
}
|
||||
} pat;
|
21
.ACM-Templates/并查集.cpp
Normal file
21
.ACM-Templates/并查集.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
//并查集 + 路径压缩 O(logn)
|
||||
int fa[N];
|
||||
void init(int n) { for (int i = 0; i <= n; i++) { fa[i] = i; } }
|
||||
int findfa(int n) { return n == fa[n] ? n : fa[n] = findfa(fa[n]); }
|
||||
inline void unite(int x, int y) {
|
||||
x = findfa(x); y = findfa(y);
|
||||
if (x != y) { fa[y] = x; }
|
||||
}
|
||||
//并查集 + 路径压缩 + 启发式合并 O(alpha(n))
|
||||
int fa[N], rnk[N];
|
||||
void init(int n) { for (int i = 0; i <= n; i++) { fa[i] = i; rnk[i] = 0; } }
|
||||
int findfa(int n) { return n == fa[n] ? n : fa[n] = findfa(fa[n]); }
|
||||
inline void unite(int x, int y) {
|
||||
x = findfa(x); y = findfa(y);
|
||||
if (x != y) {
|
||||
if (rnk[x] > rnk[y]) { fa[y] = x; }
|
||||
else { fa[x] = y; if (rnk[x] == rnk[y]) { rnk[y]++; } }
|
||||
}
|
||||
}
|
||||
//迭代路径压缩
|
||||
int findfa(int n) { while (fa[n] != n) { fa[n] = fa[fa[n]]; n = fa[n]; } return n; }
|
210
.ACM-Templates/应用.cpp
Normal file
210
.ACM-Templates/应用.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
//Joseph问题 O(n)
|
||||
int Joseph(int n, int m, int s) {
|
||||
int ret = s - 1;
|
||||
for (int i = 2; i <= n; i++) { ret = (ret + m) % i; }
|
||||
return ret + 1;
|
||||
}
|
||||
//O(logn) 0 <= k < n
|
||||
int Joseph(int n, int m, int k) {
|
||||
if (m == 1) { return n - 1; }
|
||||
for (k = k * m + m - 1; k >= n; k = k - n + (k - n) / (m - 1));
|
||||
return k;
|
||||
}
|
||||
//康托展开 fac[]为阶乘 0 <= ans
|
||||
ll Cantor(char *s) {
|
||||
ll ans = 0;
|
||||
for (int i = 0, len = strlen(s); i < len; i++) {
|
||||
int cnt = 0;
|
||||
for (int j = i + 1; j < len; j++) { if (s[j] < s[i]) { cnt++; } }
|
||||
ans += cnt * fac[len - i - 1];
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
//康托展开逆运算 1 <= k <= n!
|
||||
vector<int> revCantor(ll n, ll k) {
|
||||
vector<int> v, ret; k--;
|
||||
for (int i = 1; i <= n; i++) { v.push_back(i); }
|
||||
for (int i = n; i >= 1; i--) {
|
||||
ll t = k / fac[i - 1]; k %= fac[i - 1];
|
||||
sort(v.begin(), v.end());
|
||||
ret.push_back(v[t]); v.erase(v.begin() + t);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//求最大的全为id的子矩形面积
|
||||
//单调栈 O(nm)
|
||||
int n, m, a[N][N], h[N];
|
||||
int solve(int id) {
|
||||
int ans = 0;
|
||||
memset(h, 0, sizeof(h));
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= m; j++) { h[j] = a[i][j] == id ? h[j] + 1 : 0; }
|
||||
stack<int> st; st.push(0);
|
||||
for (int j = 1; j <= m + 1; j++) {
|
||||
while (h[j] < h[st.top()]) {
|
||||
int t = h[st.top()]; st.pop();
|
||||
int w = j - st.top() - 1;
|
||||
ans = max(ans, t * w);
|
||||
}
|
||||
st.push(j);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
//dp 悬线法 O(nm)
|
||||
int n, m, a[N][N], l[N], r[N], h[N];
|
||||
int solve(int id) {
|
||||
int ans = 0;
|
||||
for (int i = 1; i <= m; i++) { l[i] = 1; r[i] = m; h[i] = 0; }
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1, mxl = 1; j <= m; j++) {
|
||||
if (a[i][j] == id) { h[j]++; l[j] = max(l[j], mxl); }
|
||||
else { h[j] = 0; l[j] = 1; r[j] = m; mxl = j + 1; }
|
||||
}
|
||||
for (int j = m, mxr = m; j >= 1; j--) {
|
||||
if (a[i][j] == id) { r[j] = min(r[j], mxr); ans = max(ans, (r[j] - l[j] + 1) * h[j]); }
|
||||
else { mxr = j - 1; }
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
//二叉树前序 + 中序求后序遍历
|
||||
void getPost(char *pre, char *in, int len) {
|
||||
if (len == 0) { return; }
|
||||
int root = 0;
|
||||
for (; root < len && in[root] != *pre; root++);
|
||||
getPost(pre + 1, in, root);
|
||||
getPost(pre + root + 1, in + root + 1, len - root - 1);
|
||||
putchar(*pre);
|
||||
}
|
||||
//求1到n之间1的个数
|
||||
ll countOne(ll n) {
|
||||
ll ret = 0;
|
||||
for (ll m = 1; m <= n; m *= 10) {
|
||||
ll a = n / m, b = n % m;
|
||||
ret += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//求1到n所有数字的数位和的和 数位DP
|
||||
char t[N];
|
||||
ll dp[N][N][N];
|
||||
ll countDigit(char *s) {
|
||||
ll ret = 0;
|
||||
for (int i = 1, n = strlen(s); i <= n; i++) { t[i] = s[i - 1] - '0'; }
|
||||
for (int i = 0; i <= t[1]; i++) { dp[1][i][i == t[1]] = 1; }
|
||||
for (int i = 1; i < n; i++) {
|
||||
for (int j = 1; j <= 900; j++) {
|
||||
if (dp[i][j][0]) {
|
||||
for (int k = 0; k <= 9; k++) { dp[i + 1][j + k][0] += dp[i][j][0]; dp[i + 1][j + k][0] %= M; }
|
||||
}
|
||||
if (dp[i][j][1]) {
|
||||
for (int k = 0; k <= t[i + 1]; k++) { dp[i + 1][j + k][k == t[i + 1]] += dp[i][j][1]; dp[i + 1][j + k][k == t[i + 1]] %= M; }
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 1; j <= 900; j++) { ret += dp[n][j][0] * dp[n][j][1] % M; ret %= M; }
|
||||
return ret;
|
||||
}
|
||||
//树hash
|
||||
int hs[N], P[N]; //一些质数
|
||||
void dfs(int u, int p) {
|
||||
vector<int> t; t.push_back(1);
|
||||
for (int i = 0; i < (int)e[u].size(); i++) {
|
||||
int v = e[u][i];
|
||||
if (v == p) { continue; }
|
||||
dfs(v, u); t.push_back(hs[v]);
|
||||
}
|
||||
sort(t.begin(), t.end()); hs[u] = 0;
|
||||
for (int i = 0; i < (int)t.size(); i++) { hs[u] += t[i] * P[i]; }
|
||||
}
|
||||
for (int j = 1; j <= n; j++) {
|
||||
dfs(j, 0); //cout << j << ' ' << hs[j] << endl;
|
||||
}
|
||||
sort(hs[i] + 1, hs[i] + n + 1); //结果序列
|
||||
//整数划分方案数 O(n^1.5)
|
||||
int n, f[777] = {0, 1, 2, 5, 7}, g[N] = {1};
|
||||
int main() {
|
||||
for (int i = 5; i < 777; i++) { f[i] = 3 + 2 * f[i - 2] - f[i - 4]; }
|
||||
while (~scanf("%d", &n)) {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; f[j] <= i; j++) {
|
||||
if ((j + 1) >> 1 & 1) { g[i] = (g[i] + g[i - f[j]]) % M; }
|
||||
else { g[i] = ((g[i] - g[i - f[j]]) % M + M) % M; }
|
||||
}
|
||||
}
|
||||
printf("%d\n", g[n]);
|
||||
}
|
||||
}
|
||||
//所有区间gcd的预处理
|
||||
int l[N], v[N];
|
||||
void calGCD {
|
||||
for (int i = 1, j; i <= n; i++) {
|
||||
for (v[i] = a[i], j = l[i] = i; j; j = l[j] - 1) {
|
||||
v[j] = __gcd(v[j], a[i]);
|
||||
while (l[j] > 1 && __gcd(a[i], v[l[j] - 1]) == __gcd(a[i], v[j])) { l[j] = l[l[j] - 1]; }
|
||||
//[l[j]...j, i]区间内的值求gcd均为v[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
//小数转化为分数
|
||||
//把小数转化为分数, 循环部分用()表示
|
||||
void work(char str[]) {
|
||||
int len = strlen(str), cnt1 = 0, cnt2 = 0;
|
||||
ll a = 0, b = 0; bool flag = false;
|
||||
for (int i = 2; i < len; i++) {
|
||||
if (str[i] == '(') { break; }
|
||||
a = a * 10 + str[i] - '0'; cnt1++;
|
||||
}
|
||||
for (int i = 2; i < len; i++) {
|
||||
if (str[i] == '(' || str[i] == ')') { flag = true; continue; }
|
||||
b = b * 10 + str[i] - '0'; cnt2++;
|
||||
}
|
||||
ll p = b - a, q = 0; cnt2 -= cnt1;
|
||||
if (!flag) { p = b; q = 1; cnt2 = 0; }
|
||||
for (int i = 0; i < cnt2; i++) { q = q * 10 + 9; }
|
||||
for (int i = 0; i < cnt1; i++) { q = q * 10; }
|
||||
ll g = gcd(p, q);
|
||||
printf("%I64d/%I64d\n", p / g, q / g);
|
||||
}
|
||||
//分数转化为小数
|
||||
//定理: 有理数a / b(其中0 < a < b,(a, b) = 1)能表示成纯循环小数的充要条件是(b, 10) = 1
|
||||
//定理: 有理数a / b, 0 < a < b, (a, b) = 1, b = (2 ^ α) * (5 ^ β) * b1, (b1, 10) = 1,
|
||||
// b1不等于1,α,β不全为零,则a / b可以表示为纯循环小数,其不循环的位数为u = max(α, β)
|
||||
void work(int n) {
|
||||
bool flag = false;
|
||||
int ans[N] = { 0 }, f[N] = { 0, 1 }, k = 1, cnt = 0;
|
||||
if (n < 0) { n = -n; flag = 1; }
|
||||
while (k && n != 1) {
|
||||
k *= 10; ans[cnt++] = k / n; k %= n;
|
||||
if (f[k]) { break; }
|
||||
f[k] = 1;
|
||||
}
|
||||
if (flag) { printf("-"); }
|
||||
if (n == 1) { puts("1"); }
|
||||
else {
|
||||
printf("0.");
|
||||
for (int i = 0; i < cnt; i++) { printf("%d", ans[i]); }
|
||||
puts("");
|
||||
}
|
||||
}
|
||||
//水仙花数 A023052 Powerful numbers(3): numbers n that are the sum of some fixed power of their digits.
|
||||
int Nar[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 4150, 4151, 8208, 9474, 54748, 92727, 93084,
|
||||
194979, 548834, 1741725, 4210818, 9800817, 9926315, 14459929, 24678050, 24678051, 88593477
|
||||
};
|
||||
//完数 A000396 Perfect numbers n: n is equal to the sum of the proper divisors of n.
|
||||
string str[] = {
|
||||
"6",
|
||||
"28",
|
||||
"496",
|
||||
"8128",
|
||||
"33550336",
|
||||
"8589869056",
|
||||
"137438691328",
|
||||
"2305843008139952128",
|
||||
"2658455991569831744654692615953842176",
|
||||
"191561942608236107294793378084303638130997321548169216",
|
||||
"13164036458569648337239753460458722910223472318386943117783728128",
|
||||
"14474011154664524427946373126085988481573677491474835889066354349131199152128",
|
||||
"23562723457267347065789548996709904988477547858392600710143027597506337283178622239730365539602600561360255566462503270175052892578043215543382498428777152427010394496918664028644534128033831439790236838624033171435922356643219703101720713163527487298747400647801939587165936401087419375649057918549492160555646976"
|
||||
};
|
41
.ACM-Templates/排序查找.cpp
Normal file
41
.ACM-Templates/排序查找.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
//三分 求函数极大值
|
||||
const double EPS = 1e-9;
|
||||
double TS(double l, double r) {
|
||||
while (r - l > EPS) {
|
||||
double mid1 = l + (r - l) / 3.0, mid2 = r - (r - l) / 3.0;
|
||||
if (calc(mid1) > calc(mid2)) { r = mid2; }
|
||||
else { l = mid1; }
|
||||
}
|
||||
return l;
|
||||
}
|
||||
//归并排序 求逆序数
|
||||
ll cnt;
|
||||
void mergeSort(int a[], int l, int r) {
|
||||
if (l >= r) { return; }
|
||||
int mid = (l + r) >> 1;
|
||||
mergeSort(a, l, mid);
|
||||
mergeSort(a, mid + 1, r);
|
||||
vector<int> res(r - l + 1);
|
||||
int i = l, j = mid + 1, k = 0;
|
||||
while (i <= mid && j <= r) {
|
||||
if (a[i] > a[j]) { res[k++] = a[j++]; cnt += mid + 1 - i; }
|
||||
else { res[k++] = a[i++]; }
|
||||
}
|
||||
while (i <= mid) { res[k++] = a[i++]; }
|
||||
while (j <= r) { res[k++] = a[j++]; }
|
||||
for (k = l; k <= r; k++) { a[k] = res[k - l]; }
|
||||
}
|
||||
//快排
|
||||
void quickSort(int a[], int l, int r) {
|
||||
if (l >= r) { return; }
|
||||
int i = l, j = r, v = a[l];
|
||||
while (i < j) {
|
||||
while (i < j && a[j] >= v) { j--; }
|
||||
a[i] = a[j];
|
||||
while (i < j && a[i] <= v) { i++; }
|
||||
a[j] = a[i];
|
||||
}
|
||||
a[i] = v;
|
||||
quickSort(a, l, i - 1);
|
||||
quickSort(a, i + 1, r);
|
||||
}
|
634
.ACM-Templates/数学数论.cpp
Normal file
634
.ACM-Templates/数学数论.cpp
Normal file
|
@ -0,0 +1,634 @@
|
|||
//快速幂
|
||||
ll powMod(ll a, ll b, ll m) {
|
||||
ll r = 1;
|
||||
for (a %= m; b; b >>= 1) { if (b & 1) { r = r * a % m; } a = a * a % m; }
|
||||
return r;
|
||||
}
|
||||
//素数筛
|
||||
//Eratosthenes O(nloglogn)
|
||||
const int N = 10000000; //~110ms
|
||||
bitset<N> isprime;
|
||||
void getPrime() {
|
||||
isprime.set(); isprime[0] = isprime[1] = false;
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (isprime[i]) {
|
||||
for (ll j = (ll)i * i; j < N; j += i) { isprime[j] = false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//Euler O(n) prime[0]为个数
|
||||
const int N = 10000000; //~110ms
|
||||
int prime[N]; //3711111 for [2, 10^9)
|
||||
void getPrime() {
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (!prime[i]) { prime[++prime[0]] = i; }
|
||||
for (int j = 1; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
prime[prime[j] * i] = 1;
|
||||
if (i % prime[j] == 0) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//Euler O(n)
|
||||
const int N = 10000000; //~95ms
|
||||
int prime[N >> 3]; bitset<N> isprime;
|
||||
void getPrime() {
|
||||
isprime.set(); isprime[0] = isprime[1] = false;
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (isprime[i]) { prime[++prime[0]] = i; }
|
||||
for (int j = 1; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
isprime[prime[j] * i] = false;
|
||||
if (i % prime[j] == 0) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
//[a, b]区间内素数个数
|
||||
bitset<N> isprime, isprimesmall;
|
||||
ll segPrime(ll a, ll b) {
|
||||
ll ret = 0; isprime.set(); isprimesmall.set();
|
||||
for (int i = 2; (ll)i * i <= b; i++) {
|
||||
if (isprimesmall[i]) {
|
||||
for (ll j = (ll)i * i; (ll)j * j <= b; j += i) { isprimesmall[j] = false; }
|
||||
for (ll j = max(2ll, (a + i - 1) / i)) * i; j <= b; j += i) { isprime[j - a] = false; }
|
||||
}
|
||||
}
|
||||
for (ll i = 0; i <= r - l; i++) { ret += isprime[i]; }
|
||||
return ret;
|
||||
}
|
||||
//分解质因数
|
||||
ll factor[100], facCnt;
|
||||
void getFactors(ll x) {
|
||||
facCnt = 0;
|
||||
for (int i = 2, xx = sqrt(x + 0.5); i <= xx; i++) {
|
||||
if (x % i == 0) {
|
||||
factor[facCnt++] = i;
|
||||
while (x % i == 0) { x /= i; }
|
||||
}
|
||||
}
|
||||
if (x != 1) { factor[facCnt++] = x; }
|
||||
}
|
||||
//分解质因数及个数 预处理素数表
|
||||
ll factor[100][2], facCnt;
|
||||
void getFactors(ll x) {
|
||||
facCnt = 0;
|
||||
for (int i = 1; prime[i] <= x / prime[i]; i++) {
|
||||
factor[facCnt][1] = 0;
|
||||
if (x % prime[i] == 0) {
|
||||
factor[facCnt][0] = prime[i];
|
||||
while (x % prime[i] == 0) { factor[facCnt][1]++; x /= prime[i]; }
|
||||
facCnt++;
|
||||
}
|
||||
}
|
||||
if (x != 1) { factor[facCnt][0] = x; factor[facCnt++][1] = 1; }
|
||||
}
|
||||
//Miller-Rabin素性测试 素数返回true 错误(伪素数)概率为1/4^Times
|
||||
const int Times = 7;
|
||||
int WIT[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; //7 bases for n < 2^64
|
||||
ll mulMod(ll a, ll b, ll m) {
|
||||
ll r = 0;
|
||||
for (a %= m, b %= m; b; b >>= 1) { if (b & 1) { r = (r + a) % m; } a = (a << 1) % m; }
|
||||
return r;
|
||||
}
|
||||
ll powMod(ll a, ll b, ll m) {
|
||||
ll r = 1;
|
||||
for (a %= m; b; b >>= 1) { if (b & 1) { r = mulMod(r, a, m); } a = mulMod(a, a, m); }
|
||||
return r;
|
||||
}
|
||||
bool Miller_Rabin(ll n) {
|
||||
if (n == 2) { return true; }
|
||||
if (n < 2 || (n & 1) == 0) { return false; }
|
||||
ll m = n - 1; int k = 0;
|
||||
while ((m & 1) == 0) { k++; m >>= 1; }
|
||||
for (int i = 0; i < Times; i++) {
|
||||
ll a = WIT[i], x = powMod(a, m, n), y = 0;
|
||||
//ll a = rand() % (n - 1) + 1;
|
||||
for (int j = 0; j < k; j++, x = y) {
|
||||
y = mulMod(x, x, n);
|
||||
if (y == 1 && x != 1 && x != n - 1) { return false; }
|
||||
}
|
||||
if (y != 1) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//pollard rho质因素分解
|
||||
//对n进行素因子分解, 存入factor, k设置为107左右即可
|
||||
ll factor[100], facCnt; //质因素分解结果(无序)
|
||||
ll pollard_rho(ll x, ll c) {
|
||||
ll i = 1, k = 2, x0 = rand() % (x - 1) + 1, y = x0;
|
||||
while (true) {
|
||||
x0 = (mulMod(x0, x0, x) + c) % x;
|
||||
ll d = llabs(__gcd(y - x0, x));
|
||||
if (d != 1 && d != x) { return d; }
|
||||
if (y == x0) { return x; }
|
||||
if (++i == k) { y = x0; k <<= 1; }
|
||||
}
|
||||
}
|
||||
void findfac(ll n, int k = 107) {
|
||||
if (n == 1) { return; }
|
||||
if (Miller_Rabin(n)) { factor[facCnt++] = n; return; }
|
||||
ll p = n; int c = k;
|
||||
while (p >= n) { p = pollard_rho(p, c--); } //k值变化, 防止死循环
|
||||
findfac(p, k); findfac(n / p, k);
|
||||
}
|
||||
//求单个数的欧拉函数
|
||||
ll eular(ll n) {
|
||||
ll ret = 1;
|
||||
while ((n & 1) == 0) { n >>= 1; ret <<= 1; }
|
||||
for (ll i = 3; i * i <= n; i += 2) {
|
||||
if (n % i == 0) { n /= i; ret *= i - 1; while (n % i == 0) { n /= i; t *= i; } }
|
||||
}
|
||||
return n > 1 ? ret * (n - 1) : ret;
|
||||
}
|
||||
//欧拉函数筛 O(nloglogn)
|
||||
const int N = 10000000; //~400ms
|
||||
int phi[N] = {0, 1};
|
||||
void getPhi() {
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (!phi[i]) {
|
||||
for (int j = i; j < N; j += i) {
|
||||
if (!phi[j]) { phi[j] = j; } phi[j] -= phi[j] / i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//素数 + 欧拉函数筛 O(n)
|
||||
const int N = 10000000; //~150ms
|
||||
int prime[N >> 3], phi[N] = {0, 1}; bitset<N> isprime;
|
||||
void getPrimePhi() {
|
||||
isprime.set(); isprime[0] = isprime[1] = false;
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (isprime[i]) { prime[++prime[0]] = i; phi[i] = i - 1; }
|
||||
for (int j = 1, k; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
isprime[k = prime[j] * i] = false;
|
||||
if (i % prime[j] == 0) { phi[k] = phi[i] * prime[j]; break; }
|
||||
phi[k] = phi[i] * (prime[j] - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
//素数 + 莫比乌斯函数筛 O(n)
|
||||
const int N = 10000000; //150ms
|
||||
int prime[N >> 3], miu[N] = {0, 1}; bitset<N> isprime;
|
||||
void getPrimeMiu() {
|
||||
isprime.set(); isprime[0] = isprime[1] = false;
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (isprime[i]) { prime[++prime[0]] = i; miu[i] = -1; }
|
||||
for (int j = 1, k; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
isprime[k = prime[j] * i] = false;
|
||||
if (i % prime[j] == 0) { miu[k] = 0; break; }
|
||||
miu[k] = -miu[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
//素数 + 欧拉函数 + 莫比乌斯函数筛 O(n)
|
||||
const int N = 10000000; //~230ms
|
||||
int prime[N >> 3], phi[N] = {0, 1}, miu[N] = {0, 1}; bitset<N> isnprime;
|
||||
void getPrimePhiMiu() {
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (!isnprime[i]) { prime[++prime[0]] = i; phi[i] = i - 1; miu[i] = -1; }
|
||||
for (int j = 1, k; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
isnprime[k = prime[j] * i] = true;
|
||||
if (i % prime[j] == 0) { phi[k] = phi[i] * prime[j]; miu[k] = 0; break; }
|
||||
phi[k] = phi[i] * (prime[j] - 1); miu[k] = -miu[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
//素数 + 约数个数筛 O(n)
|
||||
const int N = 10000000; //~200ms
|
||||
bitset<N> isprime;
|
||||
int prime[N >> 3], faccnt[N] = {0, 1}, d[N]; //d[i]表示i的最小质因子的幂次
|
||||
void getPrimeFaccnt() {
|
||||
isprime.set(); isprime[0] = isprime[1] = false;
|
||||
for (int i = 2; i < N; i++) {
|
||||
if (isprime[i]) { prime[++prime[0]] = i; faccnt[i] = 2; d[i] = 1; }
|
||||
for (int j = 1, k; j <= prime[0] && prime[j] * i < N; j++) {
|
||||
isprime[k = prime[j] * i] = false;
|
||||
if (i % prime[j] == 0) {
|
||||
faccnt[k] = faccnt[i] / (d[i] + 1) * (d[i] + 2); d[k] = d[i] + 1; break;
|
||||
}
|
||||
faccnt[k] = faccnt[i] << 1; d[k] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//A^B的约数之和为:
|
||||
//sum = [1+p1+p1^2+...+p1^(a1*B)]*...*[1+pn+pn^2+...+pn^(an*B)].
|
||||
//等比数列求和 1+a+a^2+...+a^b
|
||||
ll sumPow(ll a, ll b, ll m) {
|
||||
ll r = 1; a %= m;
|
||||
for (ll t = 1; b; b >>= 1) {
|
||||
if (b & 1) { r = (r * a + t) % m; }
|
||||
t = t * (a + 1) % m;
|
||||
a = a * a % m;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
//求逆元(ax = 1(mod m)的x值)
|
||||
//扩展欧几里得(求ax + by = gcd(a, b)的解), 求出的x为a对b的模逆元
|
||||
ll exgcd(ll a, ll b, ll &x, ll &y) {
|
||||
if (b == 0) { x = 1; y = 0; return a; }
|
||||
ll d = exgcd(b, a % b, y, x); y -= a / b * x; return d;
|
||||
}
|
||||
//解不定方程ax + by = c 求得的只是其中一组解
|
||||
//对于不定整数方程ax + by = c, 若c mod gcd(a, b) = 0, 则该方程存在整数解, 否则不存在整数解
|
||||
//在找到ax + by = gcd(a, b)的一组解x0, y0后,可得到ax + by = c的一组解x1 = x0 * (c / gcd(a, b)), y1 = y0 * (c / gcd(a,b))
|
||||
//ax + by = c的其他整数解满足:
|
||||
//x = x1 + b / gcd(a, b) * t, y = y1 - a / gcd(a, b) * t(其中t为任意整数)
|
||||
//求ax + by = c的一组解
|
||||
bool linear_equation(int a, int b, int c, int &x, int &y) {
|
||||
int d = exgcd(a, b, x, y);
|
||||
if (c % d) { return false; }
|
||||
int k = c / d; x *= k; y *= k; return true;
|
||||
}
|
||||
//求ax = b (mod p)循环节内的所有解
|
||||
ll ans[N], cnt;
|
||||
bool linear_equation(ll a, ll b, ll p) {
|
||||
ll x, y, d = exgcd(a, p, x, y); cnt = 0;
|
||||
if (b % d) { return false; }
|
||||
x = (x % p + p) % p;
|
||||
ans[++cnt] = x * (b / d) % (p / d);
|
||||
for (int i = 1; i < d; i++) { ans[++cnt] = (ans[1] + i * p / d) % n; }
|
||||
return true;
|
||||
}
|
||||
//线性预处理逆元
|
||||
ll Inv[N] = {1, 1};
|
||||
void getInv(int m) {
|
||||
for (ll i = 2; i < m; i++) { Inv[i] = (m - m / i) * Inv[m % i] % m; }
|
||||
}
|
||||
//扩展欧几里得求逆元
|
||||
ll modReverse(ll a, ll m) {
|
||||
ll x, y, d = exgcd(a, m, x, y);
|
||||
if (d == 1) { return (x % m + m) % m; } else { return -1; }
|
||||
}
|
||||
//费马小定理, m为素数, a与m互质
|
||||
ll inv(ll a, ll m) { return powMod(a, m - 2, m); }
|
||||
//只能求0 < a < m的情况,a和m互质
|
||||
ll inv(ll a, ll m) {
|
||||
if (a == 1) { return 1; }
|
||||
return inv(m % a, m) * (m - m / a) % m;
|
||||
}
|
||||
//中国剩余定理 求模线性方程组x = a[i] (mod m[i]) m[i]可以不互质
|
||||
//[1, n]内解的个数为(n - x) / m1 + (x != 0)
|
||||
bool merge(ll a1, ll m1, ll a2, ll m2, ll &a3, ll &m3) {
|
||||
ll d = __gcd(m1, m2), c = a2 - a1;
|
||||
if (c % d != 0) { return false; }
|
||||
c = (c % m2 + m2) % m2 / d; m1 /= d; m2 /= d;
|
||||
c = c * inv(m1, m2) % m2 * m1 * d + a1;
|
||||
m3 = m1 * m2 * d; a3 = (c % m3 + m3) % m3;
|
||||
return true;
|
||||
}
|
||||
ll CRT(ll a[], ll m[], int k) {
|
||||
ll a1 = a[0], m1 = m[0];
|
||||
for (int i = 1; i < k; i++) {
|
||||
ll a2 = a[i], m2 = m[i], m3, a3;
|
||||
if (!merge(a1, m1, a2, m2, a3, m3)) { return -1; }
|
||||
a1 = a3; m1 = m3;
|
||||
}
|
||||
return (a1 % m1 + m1) % m1;
|
||||
}
|
||||
//模线性方程组 需扩展欧几里得
|
||||
//求解方程ax ≡ b (mod m) 相当于求解方程ax + my = b (x, y为整数)
|
||||
int m[10], a[10]; //模数为m, 余数为a, X % m = a
|
||||
bool solve(int &m0, int &a0, int m, int a) {
|
||||
ll y, x, d = exgcd(m0, m, x, y);
|
||||
if (abs(a - a0) % d) { return false; }
|
||||
x *= (a - a0) / d; x %= m / d;
|
||||
a0 = (x * m0 + a0); m0 *= m / d; a0 %= m0;
|
||||
if (a0 < 0) { a0 += m0; }
|
||||
return true;
|
||||
}
|
||||
//无解返回false, 有解返回true
|
||||
//解的形式最后为a0 + m0 * t (0 <= a0 < m0)
|
||||
bool MLES(int &m0, int &a0, int n) { //解为X = a0 + m0 * k
|
||||
bool flag = true; m0 = 1; a0 = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!solve(m0, a0, m[i], a[i])) { flag = false; break; }
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
//求原根
|
||||
ll fac[N];
|
||||
ll getRoot(ll n) {
|
||||
int cnt = 0;
|
||||
for (ll i = 2; i * i < n - 1; i++) { if ((n - 1) % i == 0) { fac[cnt++] = i; fac[cnt++] = (n - 1) / i; } }
|
||||
for (int i = 2, j;; i++) {
|
||||
for (j = 0; j < cnt; j++) { if (powMod(i, fac[j], n) == 1) { break; } }
|
||||
if (j == cnt) { return i; }
|
||||
}
|
||||
}
|
||||
//线性基
|
||||
//异或线性基
|
||||
//若要查询第k小子集异或和, 则把k写成二进制, 对于是1的第i位, 把从低位到高位第i个不为0的数异或进答案
|
||||
//若要判断是否有非空子集的异或和为0, 如果不存在自由基, 那么说明只有空集的异或值为0, 需要高斯消元来判断
|
||||
struct XORBase {
|
||||
int a[64];
|
||||
void clear() { memset(a, 0, sizeof(a)); }
|
||||
void ins(ll x) {
|
||||
for (int i = 62; i >= 0; i--) {
|
||||
if (x & (1 << i)) {
|
||||
if (a[i]) { x ^= a[i]; }
|
||||
else { a[i] = x; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//查询最大子集异或和
|
||||
void query() {
|
||||
ll ret = 0;
|
||||
for (int i = 62; i >= 0; i--) { ret = max(ret, ret ^ a[i]); }
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
//实数线性基
|
||||
//ins返回要插入的数是否可以被之前的数线性表示出来, 返回true表示不能, false表示可以
|
||||
int m;
|
||||
struct Base {
|
||||
double a[N][N]; bool v[N];
|
||||
void clear() { memset(a, 0, sizeof(a)); memset(v, 0, sizeof(v)); }
|
||||
bool ins(double *x) {
|
||||
for (int i = 0; i < m; i++) {
|
||||
if (fabs(x[i]) > 1e-6) {
|
||||
if (v[i]) {
|
||||
double t = x[i] / a[i][i];
|
||||
for (int j = 0; j < m; j++) { x[j] -= t * a[i][j]; }
|
||||
} else {
|
||||
v[i] = 1;
|
||||
for (int j = 0; j < m; j++) { a[i][j] = x[j]; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
//离散对数 大步小步算法 Baby-Step Giant-Step
|
||||
//BSGS(a, b, p): 求ax = b (mod p)的最小非负整数解, 若无解则返回 -1
|
||||
//rev(a, p): 扩展欧几里得求逆元
|
||||
//powMod(base, pow, mod): 快速幂
|
||||
//mulMod(a, b, mod): 快速乘(这里用快速乘是为了避免爆long long int, 实际有时可以不用)
|
||||
unordered_map<ll, ll> Hash;
|
||||
ll BSGS(ll a, ll b, ll p) {
|
||||
if (b >= p) { return -1; }
|
||||
a %= p, b %= p;
|
||||
if (!a && !b) { return 1; } //a和b都是p的倍数的话, 就相当于0^x = 0 (mod p)了, 那么最小非负整数解就是1
|
||||
if (!a) { return -1; } //如果a是p的倍数但是b不是, 就相当于0^x = t (mod p), t > 0, 无解
|
||||
Hash.clear();
|
||||
ll m = ceil(sqrt(p)), tmp = 1 % p; //tmp = a^j
|
||||
for (ll j = 0; j < m; j++) { //预处理出a^j mod p的值
|
||||
Hash[tmp] = j; tmp = mulMod(tmp, a, p);
|
||||
}
|
||||
tmp = rev(powMod(a, m, p), p); //tmp = a^(-m)
|
||||
for (ll i = 0; i < m; i++) {
|
||||
if (Hash.find(b) != Hash.end()) { return i * m + Hash[b]; }
|
||||
b = mulMod(b, tmp, p);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
//高斯消元 求线性方程组的解 O(n^3)
|
||||
//有equ个方程, var个变元, 增广矩阵行数为equ, 列数为var + 1, 下标从0开始
|
||||
int a[N][N], x[N]; //增广矩阵, 解集
|
||||
int freex[N], freenum;//自由变元 (多解枚举自由变元可以使用)
|
||||
//返回值为-1表示无解, 为0是唯一解, 否则返回自由变元个数
|
||||
int Gauss(int equ, int var) {
|
||||
int mxrow, col, k; freenum = 0;
|
||||
for (k = 0, col = 0; k < equ && col < var; k++, col++) {
|
||||
mxrow = k;
|
||||
for (int i = k + 1; i < equ; i++) { if (abs(a[i][col]) > abs(a[mxrow][col])) { mxrow = i; } }
|
||||
if (a[mxrow][col] == 0) { k--; freex[freenum++] = col; continue; } //自由变元
|
||||
if (mxrow != k) { for (int j = col; j <= var; j++) { swap(a[k][j], a[mxrow][j]); } }
|
||||
for (int i = k + 1; i < equ; i++) {
|
||||
if (a[i][col]) {
|
||||
int x = abs(a[i][col]), y = abs(a[k][col]), lcm = x / __gcd(x, y) * y, tx = lcm / x, ty = lcm / y;
|
||||
if (a[i][col] * a[k][col] < 0) { ty = -ty; }
|
||||
for (int j = col; j <= var; j++) {
|
||||
a[i][j] = a[i][j] * tx - a[k][j] * ty; //a[i][j] = (a[i][j] % M + M) % M;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = k; i < equ; i++) { if (a[i][col]) { return -1; } } //无解
|
||||
if (k < var) { return var - k; } //自由变元个数
|
||||
for (int i = var - 1; i >= 0; i--) { //唯一解,回代
|
||||
for (int j = i + 1; j < var; j++) {
|
||||
if (a[i][j]) { a[i][var] -= a[i][j] * x[j]; /*a[i][var] = (a[i][var] % M + M) % M;*/ }
|
||||
}
|
||||
//while (a[i][var] % a[i][i]) { a[i][var] += M; }
|
||||
x[i] = a[i][var] / a[i][i]; //x[i] = (a[i][var] * inv(a[i][i], M)) % M;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//高斯消元 (浮点数)
|
||||
const double eps = 1e-9;
|
||||
const int N = 205;
|
||||
double a[N][N], x[N]; //方程的左边的矩阵和等式右边的值, 求解之后x存的就是结果
|
||||
int equ, var; //方程数和未知数个数
|
||||
//返回0表示无解, 1表示有解
|
||||
int Gauss() {
|
||||
int i, j, k, col, mxrow;
|
||||
for (k = 0, col = 0; k < equ && col < var; k++, col++) {
|
||||
mxrow = k;
|
||||
for (i = k + 1; i < equ; i++) {
|
||||
if (fabs(a[i][col]) > fabs(a[mxrow][col])) { mxrow = i; }
|
||||
}
|
||||
if (fabs(a[mxrow][col]) < eps) { return 0; }
|
||||
if (k != mxrow) {
|
||||
for (j = col; j < var; j++) { swap(a[k][j], a[mxrow][j]); }
|
||||
swap(x[k], x[mxrow]);
|
||||
}
|
||||
x[k] /= a[k][col];
|
||||
for (j = col + 1; j < var; j++) { a[k][j] /= a[k][col]; }
|
||||
a[k][col] = 1;
|
||||
for (i = 0; i < equ; i++) {
|
||||
if (i != k) {
|
||||
x[i] -= x[k] * a[i][k];
|
||||
for (j = col + 1; j < var; j++) { a[i][j] -= a[k][j] * a[i][col]; }
|
||||
a[i][col] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
//自适应simpson积分
|
||||
//给定一个函数f(x), 求[a, b]区间内f(x)到x轴所形成区域的面积
|
||||
double simpson(double l, double r) {return (f(l) + f(r) + 4 * f((l + r) / 2.0)) * (r - l) / 6.0;}
|
||||
double rsimpson(double l, double r) {
|
||||
double mid = (l + r) / 2.0;
|
||||
if (fabs(simpson(l, r) - simpson(l, mid) - simpson(mid, r)) < EPS) {
|
||||
return simpson(l, mid) + simpson(mid, r);
|
||||
}
|
||||
return rsimpson(l, mid) + rsimpson(mid, r);
|
||||
}
|
||||
//FFT O(nlogn)
|
||||
//以下n必须为2的幂, op为1时是求DFT, op为-1时为求IDFT
|
||||
typedef complex<double> comp;
|
||||
const double PI = acos(-1.0);
|
||||
void fft(comp a[], int n, int op) {
|
||||
for (int i = 1, j = 0; i < n - 1; i++) {
|
||||
for (int s = n; j ^= s >>= 1, ~j & s;);
|
||||
if (i < j) { swap(a[i], a[j]); }
|
||||
}
|
||||
for (int i = 1; i < n; i <<= 1) {
|
||||
comp wn(cos(PI / i), op * sin(PI / i));
|
||||
for (int j = 0; j < n; j += i << 1) {
|
||||
comp w(1, 0);
|
||||
for (int k = 0; k < i; k++, w *= wn) {
|
||||
comp x = a[j + k], y = w * a[i + j + k];
|
||||
a[j + k] = x + y; a[i + j + k] = x - y;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (op == -1) { for (int i = 0; i < n; i++) { a[i] = comp(a[i].real() / n, a[i].imag()); } }
|
||||
}
|
||||
//求高精度乘法 HDU 1402
|
||||
comp a[N], b[N];
|
||||
char str1[N / 2], str2[N / 2];
|
||||
int sum[N];
|
||||
int main() {
|
||||
while (~scanf("%s%s", str1, str2)) {
|
||||
memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));
|
||||
int n = strlen(str1), m = strlen(str2), len = 1;
|
||||
while (len < n * 2 || len < m * 2) { len <<= 1; }
|
||||
for (int i = 0; i < n; i++) { a[i] = comp(str1[n - 1 - i] - '0', 0); }
|
||||
for (int i = 0; i < m; i++) { b[i] = comp(str2[m - 1 - i] - '0', 0); }
|
||||
fft(a, len, 1); fft(b, len, 1);
|
||||
for (int i = 0; i < len; i++) { a[i] *= b[i]; }
|
||||
fft(a, len, -1);
|
||||
for (int i = 0; i < len; i++) { sum[i] = (int)(a[i].real() + 0.5); }
|
||||
for (int i = 0; i < len; i++) { sum[i + 1] += sum[i] / 10; sum[i] %= 10; }
|
||||
len = n + m - 1;
|
||||
while (sum[len] <= 0 && len > 0) { len--; }
|
||||
for (int i = len; i >= 0; i--) { putchar(sum[i] + '0'); } puts("");
|
||||
}
|
||||
}
|
||||
//NTT O(nlogn)
|
||||
//998244353 = 119 * 2^23 + 1, 原根为3; 1004535809 = 479 * 2^21 + 1, 原根为3
|
||||
//786433 = 3 * 2^18 + 1, 原根为10; 880803841 = 105 * 2^23 + 1, 原根为26
|
||||
//P是素数且N必须是P - 1的因子
|
||||
//op为1时是求FNT, op为-1时为求IFNT
|
||||
const int P = 998244353, G = 3, N = 262144, K = 17;
|
||||
ll g[N + 5], ng[N + 5], Inv[N + 5] = {1, 1};
|
||||
void initG() {
|
||||
g[K] = powMod(G, (P - 1) / N, P); ng[K] = powMod(g[K], P - 2, P);
|
||||
for (int i = K - 1; i >= 0; i--) { g[i] = g[i + 1] * g[i + 1] % P; ng[i] = ng[i + 1] * ng[i + 1] % P; }
|
||||
for (ll i = 2; i <= N; i++) { Inv[i] = (P - P / i) * Inv[P % i] % P; }
|
||||
}
|
||||
void ntt(ll a[], int n, int op) {
|
||||
for (int i = 1, j = 0; i < n - 1; i++) {
|
||||
for (int s = n; j ^= s >>= 1, ~j & s;);
|
||||
if (i < j) { swap(a[i], a[j]); }
|
||||
}
|
||||
for (int d = 0; (1 << d) < n; d++) {
|
||||
int m = 1 << d; ll w0 = op == 1 ? g[d] : ng[d];
|
||||
for (int i = 0; i < n; i += m << 1) {
|
||||
for (int j = 0, w = 1; j < m; j++, w = w * w0 % P) {
|
||||
ll &x = a[i + j + m], &y = a[i + j], t = w * x % P;
|
||||
x = y - t; y = y + t;
|
||||
if (x < 0) { x += P; } if (y >= P) { y -= P; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (op == -1) { for (int i = 0; i < n; i++) { a[i] = a[i] * Inv[n] % P; } }
|
||||
}
|
||||
//多项式求逆元 O(nlogn) 即求B(x)满足A(X) * B(x) = 1 (mod x^n), deg(B) <= deg(A)
|
||||
void polyInv(ll a[], ll b[], int n) {
|
||||
if (n == 1) { b[0] = powMod(a[0], P - 2, P); return; }
|
||||
polyInv(a, b, n >> 1);
|
||||
int k = 1;
|
||||
while (k < n << 1) { k <<= 1; }
|
||||
for (int i = 0; i < n; i++) { tmp[i] = a[i]; }
|
||||
for (int i = n; i < k; i++) { tmp[i] = b[i] = 0; }
|
||||
ntt(tmp, k, 1); ntt(b, k, 1);
|
||||
for (int i = 0; i < k; i++) {
|
||||
b[i] = b[i] * (2 - tmp[i] * b[i] % P) % P;
|
||||
if (b[i] < 0) { b[i] += P; }
|
||||
}
|
||||
ntt(b, k, -1);
|
||||
for (int i = n; i < k; i++) { b[i] = 0; }
|
||||
}
|
||||
//多项式除法 O(nlogn) 即求D(x)和R(x)满足A(x) = D(x) * B(x) + R(x), deg(D) <= deg(A) - deg(B), deg(R) < deg(B)
|
||||
ll a0[N], b0[N];
|
||||
void polyDiv(ll a[], int n, ll b[], int m, ll d[], ll r[]) {
|
||||
int k = 1, t = n - m + 1;
|
||||
while (k < t << 1) { k <<= 1; }
|
||||
for (int i = 0; i < k; i++) { a0[i] = b0[i] = 0; }
|
||||
for (int i = 0; i < m; i++) { a0[i] = b[m - i - 1]; }
|
||||
polyInv(a0, b0, t);
|
||||
for (int i = t; i < k; i++) { b0[i] = 0; }
|
||||
for (int i = 0; i < t; i++) { a0[i] = a[n - i - 1]; }
|
||||
for (int i = t; i < k; i++) { a0[i] = 0; }
|
||||
ntt(b0, k, 1); ntt(a0, k, 1);
|
||||
for (int i = 0; i < k; i++) { a0[i] = a0[i] * b0[i] % P; }
|
||||
ntt(a0, k, -1);
|
||||
reverse(a0, a0 + t);
|
||||
for (int i = 0; i < t; i++) { d[i] = a0[i]; }
|
||||
for (k = 1; k < n << 1; k <<= 1);
|
||||
for (int i = t; i < k; i++) { a0[i] = 0; }
|
||||
for (int i = 0; i < m; i++) { b0[i] = b[i]; }
|
||||
for (int i = m; i < k; i++) { b0[i] = 0; }
|
||||
ntt(a0, k, 1); ntt(b0, k, 1);
|
||||
for (int i = 0; i < k; i++) { a0[i] = a0[i] * b0[i] % P; }
|
||||
ntt(a0, k, -1);
|
||||
for (int i = 0; i < m; i++) { r[i] = (a[i] - a0[i]) % P; }
|
||||
for (int i = m; i < k; i++) { r[i] = 0; }
|
||||
}
|
||||
//多项式求对数函数 O(nlogn) a[0] = 1
|
||||
void polyLn(ll a[], ll b[], int n) {
|
||||
polyInv(a, tmp2, n);
|
||||
int k = 1;
|
||||
while (k < n << 1) { k <<= 1; }
|
||||
for (int i = 0; i < n - 1; i++) { b[i] = a[i + 1] * (i + 1) % P; }
|
||||
for (int i = n - 1; i < k; i++) { b[i] = 0; }
|
||||
ntt(b, k, 1); ntt(tmp2, k, 1);
|
||||
for (int i = 0; i < k; i++) { b[i] = b[i] * tmp2[i] % P; }
|
||||
ntt(b, k, -1);
|
||||
for (int i = n - 1; i >= 0; i--) { b[i] = b[i - 1] * Inv[i] % P; } b[0] = 0;
|
||||
}
|
||||
//多项式求指数函数 O(nlogn) a[0] = 0
|
||||
void polyExp(ll a[], ll b[], int n) {
|
||||
if (n == 1) { b[0] = 1; return; }
|
||||
polyExp(a, b, n >> 1); polyLn(b, tmp, n);
|
||||
int k = 1;
|
||||
while (k < n << 1) { k <<= 1; }
|
||||
for (int i = 0; i < n; i++) { tmp[i] -= a[i]; if (tmp[i] < 0) { tmp[i] += P; } }
|
||||
if (++tmp[0] == P) { tmp[0] = 0; }
|
||||
for (int i = n; i < k; i++) { tmp[i] = b[i] = 0; }
|
||||
ntt(tmp, k, 1); ntt(b, k, 1);
|
||||
for (int i = 0; i < k; i++) { b[i] = b[i] * tmp[i] % P; }
|
||||
ntt(b, k, -1);
|
||||
for (int i = n; i < k; i++) { b[i] = 0; }
|
||||
}
|
||||
//多项式求平方根 O(nlogn) a[0] = 1
|
||||
void polySqrt(ll a[], ll b[], int n) {
|
||||
if (n == 1) { b[0] = 1; return; }
|
||||
polySqrt(a, b, n >> 1); polyInv(b, tmp2, n);
|
||||
int k = 1;
|
||||
while (k < n << 1) { k <<= 1; }
|
||||
for (int i = 0; i < n; i++) { tmp[i] = a[i]; }
|
||||
for (int i = n; i < k; i++) { tmp[i] = b[i] = 0; }
|
||||
ntt(tmp, k, 1); ntt(b, k, 1); ntt(tmp2, k, 1);
|
||||
for (int i = 0; i < k; i++) { b[i] = (b[i] * b[i] + tmp[i]) % P * Inv[2] % P * tmp2[i] % P; }
|
||||
ntt(b, k, -1);
|
||||
for (int i = n; i < k; i++) { b[i] = 0; }
|
||||
}
|
||||
//快速沃尔什变换 即求C[i] = sum{j ? k = i}(A[j] * B[k]) ?为任一二元逻辑位运算
|
||||
void fwt(ll a[], int n) {
|
||||
for (int d = 1; d < n; d <<= 1) {
|
||||
for (int i = 0, m = d << 1; i < n; i += m) {
|
||||
for (int j = 0; j < d; j++) {
|
||||
ll x = a[i + j], y = a[i + j + d];
|
||||
//xor: a[i + j] = x + y; a[i + j + d] = x - y;
|
||||
//and: a[i + j] = x + y;
|
||||
//or: a[i + j + d] = x + y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void ufwt(ll a[], int n) {
|
||||
for (int d = 1; d < n; d <<= 1) {
|
||||
for (int i = 0, m = d << 1; i < n; i += m) {
|
||||
for (int j = 0; j < d; j++) {
|
||||
ll x = a[i + j], y = a[i + j + d];
|
||||
//xor: a[i + j] = (x + y) >> 1; a[i + j + d] = (x - y) >> 1;
|
||||
//and: a[i + j] = x - y;
|
||||
//or: a[i + j + d] = y - x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2209
.ACM-Templates/数据结构.cpp
Normal file
2209
.ACM-Templates/数据结构.cpp
Normal file
File diff suppressed because it is too large
Load Diff
47
.ACM-Templates/日期.cpp
Normal file
47
.ACM-Templates/日期.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
//日期函数
|
||||
char *week[] = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"};
|
||||
int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
struct Date { int y, m, d; };
|
||||
//判闰年
|
||||
inline int leap(int y) {
|
||||
return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
|
||||
}
|
||||
//判合法性
|
||||
inline bool legal(Date &a) {
|
||||
if (a.m < 0 || a.m > 12) { return false; }
|
||||
if (a.m == 2) { return a.d > 0 && a.d <= 28 + leap(a.y); }
|
||||
return a.d > 0 && a.d <= days[a.m - 1];
|
||||
}
|
||||
//比较日期大小
|
||||
inline int datecmp(Date &a, Date &b) {
|
||||
if (a.y != b.y) { return a.y - b.y; }
|
||||
if (a.m != b.m) { return a.m - b.m; }
|
||||
return a.d - b.d;
|
||||
}
|
||||
//返回指定日期是星期几
|
||||
int DaysOfTheWeek(Date a) {
|
||||
if (a.m == 1 || a.m == 2) { a.m += 12; a.y--; } //1月2月当作前一年的13, 14月
|
||||
//判断是否在1752年9月3日之前
|
||||
if (a.y < 1752 || (a.y == 1752 && a.m < 9) || (a.y == 1752 && a.m == 9 && a.d < 3)) {
|
||||
return (a.d + 2 * a.m + 3 * (a.m + 1) / 5 + a.y + a.y / 4 + 5) % 7;
|
||||
} else {
|
||||
return (a.d + 2 * a.m + 3 * (a.m + 1) / 5 + a.y + a.y / 4 - a.y / 100 + a.y / 400) % 7;
|
||||
}
|
||||
}
|
||||
//日期转天数偏移
|
||||
int date2int(Date a) {
|
||||
int ret = a.y * 365 + (a.y - 1) / 4 - (a.y - 1) / 100 + (a.y - 1) / 400;
|
||||
days[1] += leap(a.y);
|
||||
for (int i = 0; i < a.m - 1; ret += days[i++]);
|
||||
days[1] = 28;
|
||||
return ret + a.d;
|
||||
}
|
||||
//天数偏移转日期
|
||||
Date int2date(int a) {
|
||||
Date ret; ret.y = a / 146097 * 400;
|
||||
for (a %= 146097; a >= 365 + leap(ret.y); a -= 365 + leap(ret.y), ret.y++);
|
||||
days[1] += leap(ret.y);
|
||||
for (ret.m = 1; a >= days[ret.m - 1]; a -= days[ret.m - 1], ret.m++);
|
||||
days[1] = 28; ret.d = a + 1;
|
||||
return ret;
|
||||
}
|
131
.ACM-Templates/矩阵.cpp
Normal file
131
.ACM-Templates/矩阵.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
//矩阵类
|
||||
template<typename T> struct mat {
|
||||
vector<T> a; int h, w;
|
||||
mat(): a(), h(), w() {}
|
||||
mat(const mat &v): a(v.a), h(v.h), w(v.w) {}
|
||||
mat(const int &_h, const int &_w): a(_h * _w), h(_h), w(_w) { }
|
||||
void init() { a.clear(); a.resize(h * w); }
|
||||
static mat e(const int &_h, const int &_w) {
|
||||
mat res(_h, _w);
|
||||
for (int i = 0, n = min(_h, _w); i < n; i++) { res[i][i] = 1; }
|
||||
return res;
|
||||
}
|
||||
static mat e(const mat &b) { return e(b.h, b.w); }
|
||||
T *operator[](const int &v) { return &a[v * w]; }
|
||||
const T *operator[](const int &v)const { return &a[v * w]; }
|
||||
mat &operator+=(const mat &b) {
|
||||
for (int i = 0, n = h * w; i < n; i++) { a[i] += b.a[i]; }
|
||||
return *this;
|
||||
}
|
||||
mat &operator-=(const mat &b) { return *this += -b; }
|
||||
mat &operator*=(const mat &b) {
|
||||
mat c(h, b.w);
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int k = 0; k < w; k++) {
|
||||
const T &tmp = (*this)[i][k];
|
||||
if (isZero(tmp)) { continue; }
|
||||
for (int j = 0; j < b.w; j++) { c[i][j] = (c[i][j] + tmp * b[k][j])/*%M*/; }
|
||||
}
|
||||
}
|
||||
swap(a, c.a); h = c.h; w = c.w; return *this;
|
||||
}
|
||||
mat operator-()const {
|
||||
mat ret(*this);
|
||||
for (int i = 0, n = h * w; i < n; i++) { ret.a[i] = -ret.a[i]; }
|
||||
return ret;
|
||||
}
|
||||
mat operator+(const mat &b)const { return mat(*this) += b; }
|
||||
mat operator-(const mat &b)const { return mat(*this) -= b; }
|
||||
mat operator*(const mat &b)const { return mat(*this) *= b; }
|
||||
mat operator^(const ll &v)const {
|
||||
mat ret(e(*this)), t(*this);
|
||||
for (ll b = v; b; b >>= 1) { if (b & 1) { ret *= t; } t *= t; }
|
||||
return ret;
|
||||
}
|
||||
bool operator==(const mat &b)const {
|
||||
if (h != b.h || w != b.w) { return false; }
|
||||
for (int i = 0, n = h * w; i < n; i++) { if (!isZero(a[i] - b.a[i])) { return false; } }
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const mat &b)const { return !(*this == b); }
|
||||
T abs(const T &v)const { return v < 0 ? -v : v; }
|
||||
bool isZero(const T &v)const { return abs(v) < 1e-9; }
|
||||
void input() { for (int i = 0, n = h * w; i < n; i++) { scanf("%I64d", &a[i]); } }
|
||||
void print()const {
|
||||
for (int i = 0, n = h * w; i < n; i++) { printf("%I64d%c", a[i], i % w == w - 1 ? '\n' : ' '); }
|
||||
}
|
||||
mat trans()const {
|
||||
mat ret(w, h);
|
||||
for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { ret[i][j] = a[j][i]; } }
|
||||
return ret;
|
||||
}
|
||||
//求逆矩阵 限double 可逆则返回true 结果在参数v中
|
||||
bool inv(mat &v)const {
|
||||
if (h != w) { return false; }
|
||||
int is[N], js[N]; v = *this;
|
||||
for (int k = 0; k < h; k++) {
|
||||
double t = 0;
|
||||
for (int i = k; i < h; i++) {
|
||||
for (int j = k; j < h; j++) {
|
||||
if (abs(v[i][j]) > t) { t = abs(v[is[k] = i][js[k] = j]); }
|
||||
}
|
||||
}
|
||||
if (isZero(t)) { return false; }
|
||||
if (is[k] != k) { for (int j = 0; j < h; j++) { swap(v[k][j], v[is[k]][j]); } }
|
||||
if (js[k] != k) { for (int i = 0; i < h; i++) { swap(v[i][k], v[i][js[k]]); } }
|
||||
v[k][k] = 1.0 / v[k][k];
|
||||
for (int j = 0; j < h; j++) { if (j != k) { v[k][j] *= v[k][k]; } }
|
||||
for (int i = 0; i < h; i++) {
|
||||
if (i != k) { for (int j = 0; j < h; j++) { if (j != k) { v[i][j] -= v[i][k] * v[k][j]; } } }
|
||||
}
|
||||
for (int i = 0; i < h; i++) { if (i != k) { v[i][k] *= -v[k][k]; } }
|
||||
}
|
||||
for (int k = h - 1; k >= 0; k--) {
|
||||
for (int j = 0; j < h; j++) { if (js[k] != k) { swap(v[k][j], v[js[k]][j]); } }
|
||||
for (int i = 0; i < h; i++) { if (is[k] != k) { swap(v[i][k], v[i][is[k]]); } }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//求行列式模M 需逆元
|
||||
ll detmod()const {
|
||||
if (h != w) { return 0; }
|
||||
ll res = 1; mat c(*this);
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = 0; j < h; j++) { c[i][j] = (c[i][j] % M + M) % M; }
|
||||
}
|
||||
for (int i = 0; i < h; i++) {
|
||||
for (int j = i; j < h; j++) {
|
||||
if (c[j][i] != 0) {
|
||||
for (int k = i; k < h; k++) { swap(c[i][k], c[j][k]); }
|
||||
if (i != j) { res = (-res + M) % M; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c[i][i] == 0) { res = -1; break; }
|
||||
for (int j = i + 1; j < h; j++) {
|
||||
//int mul = (c[j][i] * Inv[c[i][i]]) % M; //打表逆元
|
||||
ll mul = (c[j][i] * inv(c[i][i], M)) % M;
|
||||
for (int k = i; k < h; k++) { c[j][k] = (c[j][k] - (c[i][k] * mul) % M + M) % M; }
|
||||
}
|
||||
res = (res * c[i][i]) % M;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
//求行列式 限double
|
||||
double det()const {
|
||||
if (h != w) { return 0; }
|
||||
int sign = 0; double ret = 1.0; mat c(*this);
|
||||
for (int i = 0, j, k; i < h; i++) {
|
||||
if (isZero(c[i][i])) {
|
||||
for (j = i + 1; j < h && isZero(c[j][i]); j++);
|
||||
if (j == h) { return 0; }
|
||||
for (k = i; k < h; k++) { swap(c[i][k], c[j][k]); }
|
||||
sign++;
|
||||
}
|
||||
ret *= c[i][i];
|
||||
for (k = i + 1; k < h; k++) { c[i][k] /= c[i][i]; }
|
||||
for (j = i + 1; j < h; j++) { for (k = i + 1; k < h; k++) { c[j][k] -= c[j][i] * c[i][k]; } }
|
||||
}
|
||||
return sign & 1 ? -ret : ret;
|
||||
}
|
||||
};
|
41
.ACM-Templates/稀疏表.cpp
Normal file
41
.ACM-Templates/稀疏表.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
//稀疏表 Sparse Table
|
||||
//一维RMQ 预处理O(nlogn) 查询O(1)
|
||||
//返回最大值 下标从1开始 修改即可返回下标
|
||||
int p[N] = { -1}, dp[N][20];
|
||||
void initRMQ() {
|
||||
for (int i = 1; i <= n; i++) { p[i] = p[i >> 1] + 1; dp[i][0] = a[i]; }
|
||||
for (int j = 1; j <= p[n]; j++) {
|
||||
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
|
||||
dp[i][j] = dp[i][j - 1] > dp[i + (1 << (j - 1))][j - 1] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
inline int query(int l, int r) {
|
||||
if (l > r) { swap(l, r); }
|
||||
int k = p[r - l + 1];
|
||||
return dp[l][k] > dp[r - (1 << k) + 1][k] ? dp[l][k] : dp[r - (1 << k) + 1][k];
|
||||
}
|
||||
//二维RMQ 预处理O(nmlognlogm) 查询O(1) 下标从1开始
|
||||
int p[N] = { -1}, dp[N][N][9][9];
|
||||
void initRMQ() {
|
||||
for (int i = 1; i <= n; i++) {
|
||||
p[i] = p[i >> 1] + 1;
|
||||
for (int j = 1; j <= n; j++) { dp[i][j][0][0] = a[i][j]; }
|
||||
}
|
||||
for (int ii = 0; ii <= p[n]; ii++) {
|
||||
for (int jj = 0; jj <= p[m]; jj++) {
|
||||
if (ii + jj == 0) { continue; }
|
||||
for (int i = 1; i + (1 << ii) - 1 <= n; i++) {
|
||||
for (int j = 1; j + (1 << jj) - 1 <= m; j++) {
|
||||
if (ii) { dp[i][j][ii][jj] = max(dp[i][j][ii - 1][jj], dp[i + (1 << (ii - 1))][j][ii - 1][jj]); }
|
||||
else { dp[i][j][ii][jj] = max(dp[i][j][ii][jj - 1], dp[i][j + (1 << (jj - 1))][ii][jj - 1]); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inline int query(int x1, int y1, int x2, int y2) {
|
||||
if (x1 > x2) { swap(x1, x2); } if (y1 > y2) { swap(y1, y2); }
|
||||
int k1 = p[x2 - x1 + 1], k2 = p[y2 - y1 + 1]; x2 -= (1 << k1) - 1; y2 -= (1 << k2) - 1;
|
||||
return max(max(dp[x1][y1][k1][k2], dp[x1][y2][k1][k2]), max(dp[x2][y1][k1][k2], dp[x2][y2][k1][k2]));
|
||||
}
|
183
.ACM-Templates/组合数学.cpp
Normal file
183
.ACM-Templates/组合数学.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
//组合数预处理 / 杨辉三角 O(n^2)
|
||||
//C[i][i] = C[i][0] = 1
|
||||
//C[i][j] = C[i - 1][j] + C[i - 1][j - 1], 0 < j < i
|
||||
const int maxc = 105;
|
||||
ll C[maxc][maxc];
|
||||
void calC() {
|
||||
for (int i = 0; i < maxc; i++) { C[i][i] = C[i][0] = 1; }
|
||||
for (int i = 2; i < maxc; i++) {
|
||||
for (int j = 1; j < i; j++) { C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % M; }
|
||||
}
|
||||
}
|
||||
//求组合数不取模 O(n)
|
||||
ll Com(ll n, ll m) {
|
||||
if (m > n) { return 0; }
|
||||
if (m > n - m) { m = n - m; }
|
||||
ll ret = 1;
|
||||
for (ll i = 0, j = 1; i < m; i++) {
|
||||
for (ret *= n - i; j <= m && ret % j == 0; j++) { ret /= j; }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//求组合数取模
|
||||
//p <= 1e6 预处理阶乘逆元 O(min(n, p)) + O(1)
|
||||
ll fac[M] = {1, 1}, invfac[M] = {1, 1};
|
||||
void initFac(ll p) {
|
||||
for (int i = 2; i < p; i++) {
|
||||
fac[i] = fac[i - 1] * i % p; invfac[i] = (-invfac[p % i] * (p / i) % p + p) % p;
|
||||
}
|
||||
for (int i = 2; i < p; i++) { invfac[i] = invfac[i] * invfac[i - 1] % p; }
|
||||
}
|
||||
ll Com(ll n, ll m, ll p) {
|
||||
return n < m ? 0 : fac[n] * invfac[n - m] % p * invfac[m] % p;
|
||||
}
|
||||
//p <= 1e9 在线求逆元 O(nlogp)
|
||||
ll Com(ll n, ll m, ll p) {
|
||||
if (m > n) { return 0; }
|
||||
if (m > n - m) { m = n - m; }
|
||||
ll ret = 1;
|
||||
for (ll i = 1; i <= m; i++) {
|
||||
ll a = (n + i - m) % p, b = i % p;
|
||||
ret = ret * a % p * powMod(b, p - 2, p) % p;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
//Lucas定理
|
||||
ll Lucas(ll n, ll m, ll p) {
|
||||
if (n < m) { return 0; }
|
||||
if (m == 0 && n == 0) { return 1; }
|
||||
return Com(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
|
||||
}
|
||||
//第一类Stirling数 s(p, k)
|
||||
//将p个物体排成k个非空循环排列的方法数
|
||||
//s(p, k)的递推公式:s(p, k) = (p - 1) * s(p - 1, k) + s(p - 1, k - 1), 1 <= k <= p - 1
|
||||
//边界条件:s(p, 0) = 0, p >= 1, s(p, p) = 1, p >= 0
|
||||
const int maxs = 105;
|
||||
ll S[maxs][maxs];
|
||||
void calStir1() {
|
||||
S[0][0] = S[1][1] = 1;
|
||||
for (int i = 2; i < maxs; i++) {
|
||||
for (int j = 1; j <= i; j++) { S[i][j] = ((i - 1) * S[i - 1][j] + S[i - 1][j - 1]) % M; }
|
||||
}
|
||||
}
|
||||
//第二类Stirling数 S(p, k)
|
||||
//将p个物体划分成k个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数
|
||||
//k! * S(p, k)是把p个人分进k间有差别(如被标有房号)的房间(无空房)的方法数
|
||||
//S(p, k)的递推公式是:S(p, k) = k * S(p - 1, k) + S(p - 1, k - 1), 1 <= k <= p - 1
|
||||
//边界条件:S(p, 0) = 0, p >= 1, S(p, p) = 1, p >= 0
|
||||
const int maxs = 105;
|
||||
ll S[maxs][maxs];
|
||||
void calStir2() {
|
||||
S[0][0] = S[1][1] = 1;
|
||||
for (int i = 2; i < maxs; i++) {
|
||||
for (int j = 1; j <= i; j++) { S[i][j] = (j * S[i - 1][j] + S[i - 1][j - 1]) % M; }
|
||||
}
|
||||
}
|
||||
//Bell数
|
||||
//B(n)表示基数为n的集合的划分方法的数目
|
||||
//B(0) = 1, B(n + 1) = sum(C(n, k) * B(k)), 0 <= k <= n
|
||||
//每个贝尔数都是第二类Stirling数的和, 即B(n) = sum(S(n, k)), 1 <= k <= n
|
||||
//Bell三角形
|
||||
//a[0][0] = 1
|
||||
//对于n >= 1, a[n][0] = a[n - 1][n - 1]
|
||||
//对于m, n >= 1, a[n][m] = a[n][m - 1] + a[n - 1][m - 1]
|
||||
//每行首项是贝尔数,每行之和是第二类Stirling数
|
||||
//两个重要的同余性质:
|
||||
//B(p + n) = B(n) + B(n + 1) (mod p)
|
||||
//B(p^m + n) = m * B(n) + B(n + 1) (mod p)
|
||||
//p是不大于100的素数, 这样, 我们可以通过上面的性质来计算Bell数模小于100的素数值
|
||||
//Bell数模素数p的周期为: N(p) = ((p^p) - 1) / (p - 1)
|
||||
const int maxb = 105;
|
||||
ll T[maxb], B[maxb];
|
||||
void calBell() {
|
||||
B[0] = B[1] = T[0] = 1;
|
||||
for (int i = 2; i < maxb; i++) {
|
||||
T[i - 1] = B[i - 1];
|
||||
for (int j = i - 2; j >= 0; j--) { T[j] = (T[j] + T[j + 1]) % M; }
|
||||
B[i] = T[0];
|
||||
}
|
||||
}
|
||||
//计算Bell(n)对999999598 = 2 × 13 × 5281 × 7283取模 O(P^2logP)
|
||||
const int N = 7284, P = 999999598;
|
||||
ll n; int p[5] = {2, 13, 5281, 7283}, B[2][N], T[N];
|
||||
void init() {
|
||||
T[0] = T[1] = B[0][0] = 1; B[0][1] = 2;
|
||||
for (int i = 2, crt = 1; i < N; i++, crt ^= 1) {
|
||||
T[i] = B[crt][0] = B[crt ^ 1][i - 1];
|
||||
for (int j = 1; j <= i; j++) { B[crt][j] = (B[crt ^ 1][j - 1] + B[crt][j - 1]) % P; }
|
||||
}
|
||||
}
|
||||
int b[N], c[N], d[70];
|
||||
int cal(ll n, int mod) {
|
||||
int len = 0;
|
||||
for (int i = 0; i <= mod; i++) { b[i] = T[i] % mod; }
|
||||
while (n) { d[len++] = n % mod; n /= mod; }
|
||||
for (int i = 1; i < len; i++) {
|
||||
for (int j = 1; j <= d[i]; j++) {
|
||||
for (int k = 0; k < mod; k++) { c[k] = (b[k] * i + b[k + 1]) % mod; }
|
||||
c[mod] = (c[0] + c[1]) % mod;
|
||||
for (int k = 0; k <= mod; k++) { b[k] = c[k]; }
|
||||
}
|
||||
}
|
||||
return c[d[0]];
|
||||
}
|
||||
ll bell(ll n) {
|
||||
if (n < N) { return T[n]; }
|
||||
ll t = 0;
|
||||
for (int i = 0; p[i]; i++) {
|
||||
t = (t + (P / p[i]) * powMod(P / p[i], p[i] - 2, p[i]) % P * cal(n, p[i]) % P) % P;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
//卡特兰数 Catalan Number
|
||||
//Cat(1) = 1, Cat(n) = (4n - 2) * Cat(n - 1) / (n + 1) = C(2n, n) / (n + 1) = C(2n, n) - C(2n, n - 1)
|
||||
//从(0, 0)点走到(n, m)点且
|
||||
//不经过对角线的方法数(x > y): C(n + m - 1, m) - C(n + m - 1, m - 1)
|
||||
//不穿过对角线的方法数(x >= y): C(n + m, m) - C(n + m, m - 1)
|
||||
//预处理卡特兰数
|
||||
int a[105][105], b[105]; //大数, 长度
|
||||
void Catalan() {
|
||||
int i, j, len, carry, temp;
|
||||
a[1][0] = b[1] = len = 1;
|
||||
for (i = 2; i <= 100; i++) {
|
||||
for (j = 0; j < len; j++) { //乘法
|
||||
a[i][j] = a[i - 1][j] * (4 * (i - 1) + 2);
|
||||
}
|
||||
carry = 0;
|
||||
for (j = 0; j < len; j++) { //处理相乘结果
|
||||
temp = a[i][j] + carry;
|
||||
a[i][j] = temp % 10;
|
||||
carry = temp / 10;
|
||||
}
|
||||
while (carry) { //进位处理
|
||||
a[i][len++] = carry % 10;
|
||||
carry /= 10;
|
||||
}
|
||||
carry = 0;
|
||||
for (j = len - 1; j >= 0; j--) { //除法
|
||||
temp = carry * 10 + a[i][j];
|
||||
a[i][j] = temp / (i + 1);
|
||||
carry = temp % (i + 1);
|
||||
}
|
||||
while (!a[i][len - 1]) { len--; } //高位零处理
|
||||
b[i] = len;
|
||||
}
|
||||
}
|
||||
//输出大数
|
||||
void printCatalan(int n) {
|
||||
for (int i = b[n] - 1; i >= 0; i--) { printf("%d", a[n][i]); }
|
||||
}
|
||||
//各种情况下小球放盒子的方案数
|
||||
//k个球 m个盒子 是否允许有空盒子 方案数
|
||||
//不同 不同 是 m^k
|
||||
//不同 不同 否 m!*Stirling2(k, m)
|
||||
//不同 相同 是 ∑(m, i=1)Stirling2(k, i)
|
||||
//不同 相同 否 Stirling2(k, m)
|
||||
//相同 不同 是 C(m + k - 1, k)
|
||||
//相同 不同 否 C(k - 1, m - 1)
|
||||
//相同 相同 是 1/(1-x)(1-x^2)...(1-x^m)的x^k项的系数
|
||||
//相同 相同 否 x^m/(1-x)(1-x^2)...(1-x^m)的x^k项的系数
|
||||
//错排公式
|
||||
//D(1) = 0, D(2) = 1, D(n) = (n - 1)(D(n - 2) + D(n - 1))
|
||||
//扩展 Cayley 公式
|
||||
//对于n个点, m个连通块的图, 假设每个连通块有a[i]个点, 那么用s - 1条边把它连通的方案数为n^(s-2)a[1]a[2]...a[m]
|
28
.ACM-Templates/莫队.cpp
Normal file
28
.ACM-Templates/莫队.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
//莫队算法 O(n^1.5)
|
||||
//莫队算法是离线处理一类区间不修改查询类问题的算法
|
||||
//如果你知道了[L,R]的答案, 你可以在O(1)的时间下得到[L,R-1]和[L,R+1]和[L-1,R]和[L+1,R]的答案的话, 就可以使用莫队算法
|
||||
//lydsy 2038: [2009国家集训队]小Z的袜子(hose)
|
||||
int n, m;
|
||||
int c[N], pos[N];
|
||||
int ansup[N], ansdn[N], cnt[N];
|
||||
struct Node {
|
||||
int l, r, id;
|
||||
bool operator<(const Node &b)const { return pos[l] < pos[b.l] || (pos[l] == pos[b.l] && r < b.r); }
|
||||
} q[N];
|
||||
int main() {
|
||||
while (~scanf("%d%d", &n, &m)) {
|
||||
memset(cnt, 0, sizeof(cnt)); ll ans = 0;
|
||||
for (int i = 1, nn = ceil(sqrt(n)); i <= n; i++) { scanf("%d", &c[i]); pos[i] = (i - 1) / nn; }
|
||||
for (int i = 0; i < m; i++) { scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i; }
|
||||
sort(q, q + m);
|
||||
for (int i = 0, l = 1, r = 0; i < m; i++) {
|
||||
for (; l > q[i].l;) { ans += cnt[c[--l]]++; }
|
||||
for (; l < q[i].l;) { ans -= --cnt[c[l++]]; }
|
||||
for (; r < q[i].r;) { ans += cnt[c[++r]]++; }
|
||||
for (; r > q[i].r;) { ans -= --cnt[c[r--]]; }
|
||||
ll dn = (r - l + 1LL) * (r - l) >> 1, gcd = __gcd(ans, dn);
|
||||
ansup[q[i].id] = ans / gcd; ansdn[q[i].id] = dn / gcd;
|
||||
}
|
||||
for (int i = 0; i < m; i++) { printf("%d/%d\n", ansup[i], ansdn[i]); }
|
||||
}
|
||||
}
|
1393
.ACM-Templates/计算几何.cpp
Normal file
1393
.ACM-Templates/计算几何.cpp
Normal file
File diff suppressed because it is too large
Load Diff
71
.ACM-Templates/输入输出.cpp
Normal file
71
.ACM-Templates/输入输出.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
//关闭同步
|
||||
ios_base::sync_with_stdio(false); cin.tie(NULL);
|
||||
//输入一个非负整数
|
||||
template<typename T> void read(T &x) {
|
||||
char c;
|
||||
while (!isdigit(c = getchar()));
|
||||
for (x = 0; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; }
|
||||
}
|
||||
//输入一个整数
|
||||
template<typename T> void read(T &x) {
|
||||
char c; bool neg = false;
|
||||
while ((c = getchar()) != '-' && !isdigit(c));
|
||||
if (c == '-') { neg = true; c = getchar(); }
|
||||
for (x = 0; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; }
|
||||
if (neg) { x = -x; }
|
||||
}
|
||||
//输入一个数(int, long long, float, double)
|
||||
template<typename T> void read(T &x) {
|
||||
char c; bool neg = false;
|
||||
while ((c = getchar()) != '-' && c != '.' && !isdigit(c));
|
||||
if (c == '-') { neg = true; c = getchar(); }
|
||||
for (x = 0; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; }
|
||||
if (c == ' ' || c == '\n' || c == EOF) { if (neg) { x = -x; } return; }
|
||||
for (T bit = 0.1; isdigit(c = getchar()); bit *= 0.1) { x += (c - '0') * bit; }
|
||||
if (neg) { x = -x; }
|
||||
}
|
||||
//空格作为分隔 读取一行的整数
|
||||
const int BUFSIZE = 1 << 20;
|
||||
char BUF[BUFSIZE];
|
||||
void readln(int a[]) {
|
||||
int i = 0; gets(BUF);
|
||||
for (char *p = strtok(BUF, " "); p; p = strtok(NULL, " ")) { sscanf(p, "%d", &a[i++]); }
|
||||
}
|
||||
//输出一个正整数
|
||||
template<typename T> void printn(T x) {
|
||||
if (x > 9) { printn(x / 10); }
|
||||
putchar(x % 10 + '0');
|
||||
}
|
||||
//配合fread
|
||||
const int BUFSIZE = 1 << 20;
|
||||
char BUF[BUFSIZE + 1], *S, *T; bool eof;
|
||||
inline char gc() {
|
||||
if (S == T) {
|
||||
S = BUF; T = BUF + fread(BUF, 1, BUFSIZE, stdin);
|
||||
if (S == T) { eof = true; return EOF; }
|
||||
}
|
||||
return *S++;
|
||||
}
|
||||
template<typename T> void ni(T &x) {
|
||||
char c; bool neg = false; x = 0;
|
||||
while ((c = gc()) != '-' && !isdigit(c));
|
||||
if (eof) { return; }
|
||||
if (c == '-') { neg = true; c = gc(); }
|
||||
for (; isdigit(c); c = gc()) { x = x * 10 + c - '0'; }
|
||||
if (neg) { x = -x; }
|
||||
}
|
||||
void nd(double &x) {
|
||||
char c; bool neg = false; x = 0;
|
||||
while ((c = gc()) != '-' && !isdigit(c));
|
||||
if (eof) { return; }
|
||||
if (c == '-') { neg = true; c = gc(); }
|
||||
for (; isdigit(c); c = gc()) { x = x * 10 + c - '0'; }
|
||||
if (c == '.') { for (double bit = 0.1; isdigit(c = gc()); bit *= 0.1) { x += (c - '0') * bit; } }
|
||||
if (neg) { x = -x; }
|
||||
}
|
||||
void ns(char *s) {
|
||||
char c;
|
||||
while (isspace(c = gc()));
|
||||
if (eof) { return; }
|
||||
for (; !isspace(c); c = gc()) { *s++ = c; } *s = 0;
|
||||
}
|
195
.ACM-Templates/高精度.cpp
Normal file
195
.ACM-Templates/高精度.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
//高精度整数
|
||||
const int BASE = 1000000000;
|
||||
const int BASEDIGITS = 9;
|
||||
struct bint {
|
||||
vector<int> s; char sign;
|
||||
bint(): s(), sign(1) {}
|
||||
bint(const ll &v): s(), sign(v < 0 ? -1 : 1) {
|
||||
for (ll t = v < 0 ? -v : v; t; t /= BASE) { s.push_back(t % BASE); }
|
||||
}
|
||||
bint(const string &v): s(), sign(1) {
|
||||
int beg = 0;
|
||||
for (; beg < (int)v.size() && (v[beg] == '-' || v[beg] == '+'); beg++) {
|
||||
if (v[beg] == '-') { sign = -1; }
|
||||
}
|
||||
for (int i = (int)v.size() - 1, x, j; i >= beg; i -= BASEDIGITS) {
|
||||
for (x = 0, j = max(beg, i - BASEDIGITS + 1); j <= i; x = x * 10 + v[j++] - '0');
|
||||
s.push_back(x);
|
||||
}
|
||||
trim();
|
||||
}
|
||||
bint &operator=(const bint &v) { sign = v.sign; s = v.s; return *this; }
|
||||
bint &operator+=(const bint &v) {
|
||||
if (sign == v.sign) {
|
||||
for (int i = 0, is = 0, len = max(s.size(), v.s.size()); i < len || is; i++) {
|
||||
if (i == (int)s.size()) { s.push_back(0); }
|
||||
s[i] += is + (i < (int)v.s.size() ? v.s[i] : 0);
|
||||
if ((is = s[i] >= BASE)) { s[i] -= BASE; }
|
||||
}
|
||||
return *this;
|
||||
} else { return *this -= -v; }
|
||||
}
|
||||
bint &operator-=(const bint &v) {
|
||||
if (sign == v.sign) {
|
||||
if (cmp(v, 0) != -1) {
|
||||
for (int i = 0, is = 0; i < (int)v.s.size() || is; i++) {
|
||||
s[i] -= is + (i < (int)v.s.size() ? v.s[i] : 0);
|
||||
if ((is = s[i] < 0)) { s[i] += BASE; }
|
||||
}
|
||||
trim(); return *this;
|
||||
} else { return *this = -(bint(v) -= *this); }
|
||||
} else { return *this += -v; }
|
||||
}
|
||||
bint &operator*=(const bint &v) {
|
||||
vector<ll> num(s.size() + v.s.size());
|
||||
for (int i = 0; i < (int)s.size(); i++) {
|
||||
for (int j = 0; j < (int)v.s.size(); j++) {
|
||||
num[i + j] += (ll)s[i] * v.s[j];
|
||||
if (num[i + j] >= BASE) { num[i + j + 1] += num[i + j] / BASE; num[i + j] %= BASE; }
|
||||
}
|
||||
}
|
||||
sign *= v.sign; s.resize(num.size());
|
||||
for (int i = 0; i < (int)num.size(); i++) { s[i] = num[i]; }
|
||||
trim(); return *this;
|
||||
}
|
||||
bint &operator/=(const bint &v) { return *this = divmod(*this, v).first; }
|
||||
bint &operator%=(const bint &v) { return *this = divmod(*this, v).second; }
|
||||
bint operator-()const { bint ret(*this); ret.sign = -sign; return ret; }
|
||||
bint operator+(const bint &v)const { return bint(*this) += v; }
|
||||
bint operator-(const bint &v)const { return bint(*this) -= v; }
|
||||
bint operator*(const bint &v)const { return bint(*this) *= v; }
|
||||
bint operator/(const bint &v)const { return divmod(*this, v).first; }
|
||||
bint operator%(const bint &v)const { return divmod(*this, v).second; }
|
||||
bint operator^(const ll &v)const {
|
||||
bint ret(1), t(*this);
|
||||
for (ll b = v; b; b >>= 1) { if (b & 1) { ret *= t; } t *= t; }
|
||||
return ret;
|
||||
}
|
||||
//乘除法辅助函数
|
||||
friend pair<bint, bint> divmod(const bint &a, const bint &b) {
|
||||
int norm = BASE / (b.s.back() + 1);
|
||||
bint x = a.abs().mul(norm), y = b.abs().mul(norm), q, r; q.s.resize(x.s.size());
|
||||
for (int i = (int)x.s.size() - 1; i >= 0; i--) {
|
||||
r = r.mul(BASE); r += x.s[i];
|
||||
int s1 = r.s.size() <= y.s.size() ? 0 : r.s[y.s.size()];
|
||||
int s2 = r.s.size() + 1 <= y.s.size() ? 0 : r.s[y.s.size() - 1];
|
||||
int d = ((ll)BASE * s1 + s2) / y.s.back();
|
||||
r -= y.mul(d);
|
||||
while (r.cmp(0, 1) == -1) { r += y; --d; }
|
||||
q.s[i] = d;
|
||||
}
|
||||
q.sign = a.sign * b.sign; q.trim(); r.sign = a.sign; r.trim();
|
||||
return make_pair(q, r.div(norm));
|
||||
}
|
||||
bint mul(int v)const {
|
||||
bint ret(*this);
|
||||
if (v < 0) { ret.sign = -ret.sign; v = -v; }
|
||||
for (int i = 0, is = 0; i < (int)ret.s.size() || is; i++) {
|
||||
if (i == (int)s.size()) { ret.s.push_back(0); }
|
||||
ll a = ret.s[i] * (ll)v + is; is = a / BASE; ret.s[i] = a % BASE;
|
||||
}
|
||||
ret.trim(); return ret;
|
||||
}
|
||||
bint div(int v)const {
|
||||
bint ret(*this);
|
||||
if (v < 0) { ret.sign = -ret.sign; v = -v; }
|
||||
for (int i = (int)ret.s.size() - 1, rem = 0; i >= 0; i--) {
|
||||
ll a = ret.s[i] + rem * (ll)BASE; ret.s[i] = a / v; rem = a % v;
|
||||
}
|
||||
ret.trim(); return ret;
|
||||
}
|
||||
int mod(int v)const {
|
||||
if (v < 0) { v = -v; }
|
||||
int m = 0;
|
||||
for (int i = (int)s.size() - 1; i >= 0; i--) { m = (s[i] + m * (ll)BASE) % v; }
|
||||
return m * sign;
|
||||
}
|
||||
bool operator<(const bint &v)const { return cmp(v) < 0; }
|
||||
bool operator>(const bint &v)const { return cmp(v) > 0; }
|
||||
bool operator<=(const bint &v)const { return cmp(v) <= 0; }
|
||||
bool operator>=(const bint &v)const { return cmp(v) >= 0; }
|
||||
bool operator==(const bint &v)const { return cmp(v) == 0; }
|
||||
bool operator!=(const bint &v)const { return cmp(v) != 0; }
|
||||
int cmp(const bint &v, bool is = 1)const {
|
||||
if (is) { if (sign > v.sign) { return 1; } if (sign < v.sign) { return -1; } }
|
||||
int d = sign > 0 || !is ? 1 : -1;
|
||||
if (s.size() > v.s.size()) { return d; }
|
||||
if (s.size() < v.s.size()) { return -d; }
|
||||
for (int i = (int)s.size() - 1; i >= 0; i--) {
|
||||
if (s[i] > v.s[i]) { return d; } if (s[i] < v.s[i]) { return -d; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bint abs()const { bint ret(*this); ret.sign *= ret.sign; return ret; }
|
||||
void trim() {
|
||||
while (!s.empty() && !s.back()) { s.pop_back(); }
|
||||
if (s.empty()) { sign = 1; }
|
||||
}
|
||||
void print()const {
|
||||
if (sign == -1) { putchar('-'); }
|
||||
printf("%d", s.empty() ? 0 : s.back());
|
||||
for (int i = (int)s.size() - 2; i >= 0; i--) { printf("%09d", s[i]); }
|
||||
}
|
||||
friend istream &operator>>(istream &in, bint &v) { string s; in >> s; v = s; return in; }
|
||||
friend ostream &operator<<(ostream &out, const bint &v) {
|
||||
if (v.sign == -1) { out << '-'; }
|
||||
out << setfill('0') << (v.s.empty() ? 0 : v.s.back());
|
||||
for (int i = (int)v.s.size() - 2; i >= 0; i--) { out << setw(BASEDIGITS) << v.s[i]; }
|
||||
return out << setfill(' ');
|
||||
}
|
||||
string toString()const {
|
||||
if (s.empty()) { return "0"; }
|
||||
string ret, x;
|
||||
if (sign == -1) { ret += '-'; }
|
||||
for (int o = s[s.size() - 1]; o; o /= 10) { x += o % 10 + '0'; }
|
||||
for (int i = (int)x.size() - 1; i >= 0; i--) { ret += x[i]; }
|
||||
for (int i = (int)s.size() - 2; i >= 0; i--) {
|
||||
x.clear();
|
||||
for (int j = 0, p = s[i]; j < BASEDIGITS; j++, p /= 10) { x += p % 10 + '0'; }
|
||||
for (int j = BASEDIGITS - 1; j >= 0; j--) { ret += x[j]; }
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
operator bool()const { return s.size() && !(s.size() == 1 && !s[0]); }
|
||||
//高精度开方
|
||||
bint sqrt()const {
|
||||
bint ret, t(*this); ret.s.resize((t.s.size() + 1) >> 1);
|
||||
for (int i = (int)ret.s.size() - 1; i >= 0; i--) {
|
||||
int l = 0, r = BASE - 1, mid = ret.s[i] = (l + r + 1) >> 1;
|
||||
while (l < r) {
|
||||
if (comp(ret, mid, i - 1, t)) { r = mid - 1; }
|
||||
else { l = mid; }
|
||||
mid = ret.s[i] = (l + r + 1) >> 1;
|
||||
}
|
||||
sub(t, ret, mid, i - 1); ret.s[i] += mid;
|
||||
}
|
||||
for (int i = 0; i < (int)ret.s.size(); i++) { ret.s[i] >>= 1; }
|
||||
ret.trim(); return ret;
|
||||
}
|
||||
void sub(bint &a, const bint &b, const int &k, const int &d)const {
|
||||
for (int i = d + 1, l = b.s.size() + d; i <= l; i++) {
|
||||
ll tmp = a.s[i] - (ll)b.s[i - d - 1] * k;
|
||||
if (tmp < 0) { a.s[i + 1] += (tmp - BASE + 1) / BASE; a.s[i] = tmp - (tmp - BASE + 1) / BASE * BASE; }
|
||||
else { a.s[i] = tmp; }
|
||||
}
|
||||
for (int i = b.s.size() + d + 1; i < (int)a.s.size() && a.s[i] < 0; i++) {
|
||||
a.s[i + 1] += (a.s[i] - BASE + 1) / BASE; a.s[i] -= (a.s[i] - BASE + 1) / BASE * BASE;
|
||||
}
|
||||
a.trim();
|
||||
}
|
||||
bool comp(const bint &a, const int &c, const int &d, const bint &b)const {
|
||||
int l = -(BASE << 1); ll t = 0;
|
||||
if (b.s.size() < a.s.size() + d && c) { return true; }
|
||||
for (int i = (int)b.s.size() - 1; i > d; i--) {
|
||||
t = t * BASE + (ll)(i - d - 1 < (int)a.s.size() ? a.s[i - d - 1] : 0) * c - b.s[i];
|
||||
if (t > 0) { return true; }
|
||||
if (t < l) { return false; }
|
||||
}
|
||||
for (int i = d - 1; i >= 0; i--) {
|
||||
t = t * BASE - b.s[i];
|
||||
if (t > 0) { return true; }
|
||||
if (t < l) { return false; }
|
||||
}
|
||||
return t > 0;
|
||||
}
|
||||
};
|
218
.ACM-Templates/黑科技.cpp
Normal file
218
.ACM-Templates/黑科技.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
//加栈
|
||||
//G++
|
||||
int main() {
|
||||
int sz = 100 << 20; //100MB
|
||||
char *p = (char *)malloc(sz) + sz;
|
||||
__asm__ __volatile__(
|
||||
#if __x86_64__ || __ppc64__ || _WIN64 //64-bit
|
||||
"movq %0, %%rsp\n pushq $_exit\n"
|
||||
#else //32-bit
|
||||
"movl %0, %%esp\n pushl $_exit\n"
|
||||
#endif
|
||||
:: "r"(p));
|
||||
//......
|
||||
exit(0);
|
||||
}
|
||||
//VC++ 100MB
|
||||
#pragma comment(linker, "/STACK:102400000,102400000")
|
||||
|
||||
//位运算
|
||||
//把右边连续的1变成0 | (100101111->100100000) | x and (x+1)
|
||||
//把右起第一个0变成1 | (100101111->100111111) | x or (x+1)
|
||||
//把右边连续的0变成1 | (11011000->11011111) | x or (x-1)
|
||||
//取右边连续的1 | (100101111->1111) | (x xor (x+1)) shr 1
|
||||
//去掉右起第一个1的左边 | (100101000->1000) | x and (x xor (x-1))
|
||||
//枚举i的非空子集j
|
||||
for (int j = i; j; j = (j - 1) & i);
|
||||
|
||||
//builtin函数
|
||||
int __builtin_ffs(int x);
|
||||
int __builtin_ffsll(long long);
|
||||
//Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero.
|
||||
int __builtin_clz(unsigned int x);
|
||||
int __builtin_clzll(unsigned long long);
|
||||
//Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
|
||||
int __builtin_ctz(unsigned int x);
|
||||
int __builtin_ctzll(unsigned long long);
|
||||
//Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined.
|
||||
int __builtin_clrsb(int x);
|
||||
int __builtin_clrsbll(long long);
|
||||
//Returns the number of leading redundant sign bits in x, i.e. the number of bits following the most significant bit that are identical to it. There are no special cases for 0 or other values.
|
||||
int __builtin_popcount(unsigned int x);
|
||||
int __builtin_popcountll(unsigned long long);
|
||||
//Returns the number of 1-bits in x.
|
||||
int __builtin_parity(unsigned int x);
|
||||
int __builtin_parityll(unsigned long long);
|
||||
//Returns the parity of x, i.e. the number of 1-bits in x modulo 2.
|
||||
uint32_t __builtin_bswap32(uint32_t x);
|
||||
uint64_t __builtin_bswap64(uint64_t x);
|
||||
//Returns x with the order of the bytes reversed; for example, 0xaabb becomes 0xbbaa. Byte here always means exactly 8 bits.
|
||||
|
||||
//rope O(log(len))
|
||||
#include <ext/rope>
|
||||
using namespace __gnu_cxx;
|
||||
|
||||
//pb_ds库
|
||||
//http://gaotianyu1350.gitcafe.io/2015/02/17/pbds/
|
||||
//priority_queue
|
||||
//定义
|
||||
//包含:ext/pb_ds/priority_queue.hpp
|
||||
//声明:__gnu_pbds::priority_queue<T>
|
||||
//模板参数:
|
||||
//template<typename Value_Type,
|
||||
// typename Cmp_Fn = std::less<Value_Type>,
|
||||
// typename Tag = pairing_heap_tag,
|
||||
// typename Allocator = std::allocator<char>>
|
||||
//class priority_queue;
|
||||
//Value_Type:类型
|
||||
//Cmp_Fn:自定义比较器
|
||||
//Tag:堆的类型。可以是binary_heap_tag(二叉堆)binomial_heap_tag(二项堆)rc_binomial_heap_tag pairing_heap_tag(配对堆)thin_heap_tag
|
||||
//Allocator:不用管
|
||||
//使用
|
||||
//相比STL中的priority_queue,可以
|
||||
//用begin()和end()获取迭代器从而遍历
|
||||
//删除单个元素void erase(point_iterator)
|
||||
//增加或减少某一元素的值void modify(point_iterator, const_reference)
|
||||
//合并void join(priority_queue &other),把other合并到*this,并把other清空
|
||||
//性能分析
|
||||
//五种操作:push、pop、modify、erase、join
|
||||
//pairing_heap_tag:push和joinO(1),其余均摊O(logn)
|
||||
//binary_heap_tag:只支持push和pop,均为均摊O(logn)
|
||||
//binomial_heap_tag:push为均摊O(1),其余为O(logn)
|
||||
//rc_binomial_heap_tag:push为O(1),其余为O(logn)
|
||||
//thin_heap_tag:push为O(1),不支持join,其余为O(logn);但是如果只有increase_key,modify均摊O(1)
|
||||
//不支持不是不能用,而是用起来很慢
|
||||
//大致结论:
|
||||
//对于优化Dijkstra算法,pairing_heap_tag严格快于thin_heap_tag,速度与手写数据结构相当
|
||||
//线段树大法好
|
||||
//binary_heap_tag在绝大多数情况下优于std::priority_queue
|
||||
//pairing_heap_tag在绝大多数情况下优于binomial_heap_tag和rc_binomial_heap_tag
|
||||
//只有push,pop和join操作时,binary_heap_tag速度较快
|
||||
//在有modify操作时,可以考虑pairing_heap_tag,thin_heap_tag或手写数据结构
|
||||
|
||||
//Tree
|
||||
//定义
|
||||
//包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/tree_policy.hpp
|
||||
//声明:__gnu_pbds::tree<Key, T>
|
||||
//模板参数:
|
||||
//template<typename Key,
|
||||
// typename Mapped,
|
||||
// typename Cmp_Fn = std::less<Key>,
|
||||
// typename Tag = rb_tree_tag,
|
||||
// template<typename Const_Node_Iterator,
|
||||
// typename Node_Iterator,
|
||||
// typename Cmp_Fn_,
|
||||
// typename Allocator_ >
|
||||
// class Node_Update = null_tree_node_update,
|
||||
// typename Allocator = std::allocator<char>>
|
||||
//class tree;
|
||||
//Tag:tree的类型,可以是rb_tree_tag,splay_tree_tag,ov_tree_tag
|
||||
//Node_Update:可以为空,也可以用pb_ds自带的tree_order_statistics_node_update,这样这个tree就会获得两个函数find_by_order和order_of_key
|
||||
//iterator find_by_order(size_type order):找第order + 1小的元素的迭代器,如果order太大会返回end()
|
||||
//size_type order_of_key(const_key_reference r_key):询问这个tree中有多少比r_key小的元素
|
||||
//使用
|
||||
//与map使用方法基本相同,包括begin(),end(),size(),empty(),clear(),find(const Key),
|
||||
//lower_bound(const Key),upper_bound(const Key),erase(iterator),erase(const Key),
|
||||
//insert(const pair<Key, T>),operator[](const Key)
|
||||
//如果想改成set,只需要将第二个参数Mapped改为null_type(在4.4.0及以下版本的编译器中应用null_mapped_type)就可以了。此时迭代器指向的类型会从pair变成Key,和set几乎没有区别
|
||||
//当然还有一些其他用法,如:
|
||||
//void join(tree &other):把other中所有元素移动到*this上(值域不能相交,否则会抛出异常)
|
||||
//void split(const_key_reference r_key, tree &other):清空other,然后把*this中所有大于r_key的元素移动到other
|
||||
//自定义Node_Update使用方法
|
||||
//自带的tree_order_statistics_node_update统计的是子树size
|
||||
//稍加修改就可以统计容易合并的任意信息
|
||||
//以下代码实现了区间求和
|
||||
template<class Node_CItr, class Node_Itr, class Cmp_Fn, class _Alloc>
|
||||
struct my_node_update {
|
||||
virtual Node_CItr node_begin()const = 0;
|
||||
virtual Node_CItr node_end()const = 0;
|
||||
typedef int metadata_type; //节点上记录的额外信息的类型
|
||||
inline void operator()(Node_Itr it, Node_CItr end_it) {
|
||||
Node_Itr l = it.get_l_child(), r = it.get_r_child();
|
||||
int left = 0, right = 0;
|
||||
if (l != end_it) { left = l.get_metadata(); }
|
||||
if (r != end_it) { right = r.get_metadata(); }
|
||||
const_cast<metadata_type &>(it.get_metadata()) = left + right + (*it)->second;
|
||||
}
|
||||
inline int prefix_sum(int x) {
|
||||
int ans = 0;
|
||||
Node_CItr it = node_begin();
|
||||
while (it != node_end()) {
|
||||
Node_CItr l = it.get_l_child(), r = it.get_r_child();
|
||||
if (Cmp_Fn()(x, (*it)->first)) { it = l; }
|
||||
else {
|
||||
ans += (*it)->second;
|
||||
if (l != node_end()) { ans += l.get_metadata(); }
|
||||
it = r;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
inline int interval_sum(int l, int r) {
|
||||
return prefix_sum(r) - prefix_sum(l - 1);
|
||||
}
|
||||
};
|
||||
int main() {
|
||||
tree<int, int, std::less<int>, rb_tree_tag, my_node_update> T;
|
||||
T[2] = 100; T[3] = 1000; T[4] = 10000;
|
||||
printf("%d\n%d\n", T.interval_sum(3, 4), T.prefix_sum(3));
|
||||
}
|
||||
//注意:
|
||||
//对Node_Itr可以做的事情:用get_l_child和get_r_child获取左右儿子,用两个星号(一个星号只是获取了iterator)获取节点信息,用get_metadata获取节点额外信息
|
||||
//operator()的功能是将节点it的信息更新,传入的end_it表示空节点
|
||||
//性能分析
|
||||
//和手写数据结构差不多,rb_tree_tag要更快
|
||||
|
||||
//Hash
|
||||
//定义
|
||||
//包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/hash_policy.hpp
|
||||
//声明:
|
||||
//__gnu_pbds::cc_hash_table<Key, Mapped>
|
||||
//__gnu_pbds::gp_hash_table<Key, Mapped>
|
||||
//使用
|
||||
//支持find和operator[]
|
||||
|
||||
//Trie
|
||||
//定义
|
||||
//包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/trie_policy.hpp
|
||||
//声明:__gnu_pbds::trie<Key, Mapped>
|
||||
//模板参数:
|
||||
//template<typename Key,
|
||||
// typename Mapped,
|
||||
// typename Cmp_Fn = std::less<Key>,
|
||||
// typename Tag = pat_trie_tag,
|
||||
// template<typename Const_Node_Iterator,
|
||||
// typename Node_Iterator,
|
||||
// typename E_Access_Traits_,
|
||||
// typename Allocator_>
|
||||
// class Node_Update = null_trie_node_update,
|
||||
// typename Allocator = std::allocator<char>>
|
||||
//class trie;
|
||||
//Key is the key type.
|
||||
//Mapped is the mapped-policy, and is explained in Tutorial::Associative Containers::Associative Containers Others than Maps.
|
||||
//E_Access_Traits is described in Element-Access Traits.
|
||||
//Tag specifies which underlying data structure to use, and is described shortly.
|
||||
//Node_Update is a policy for updating node invariants. This is described in Node Invariants.
|
||||
//Allocator is an allocator type.
|
||||
//功能:
|
||||
//pair<const_iterator, const_iterator> prefix_range(key_const_reference)
|
||||
//Finds the const iterator range corresponding to all values whose prefixes match r_key
|
||||
//如果你想用这个函数,trie的模板参数得这么写trie<string, [your type here], string_trie_e_access_traits<>, pat_trie_tag, trie_prefix_search_node_update>
|
||||
|
||||
//List(用作multimap/multiset)
|
||||
//定义
|
||||
//包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/list_update_policy.hpp
|
||||
//声明:__gnu_pbds::list_update<Key, Mapped>
|
||||
//模板参数:
|
||||
//template<typename Key,
|
||||
// typename Mapped,
|
||||
// typename Eq_Fn = std::equal_to<Key>,
|
||||
// typename Update_Policy = move_to_front_lu_policy<>,
|
||||
// typename Allocator = std::allocator<char>>
|
||||
//class list_update;
|
||||
|
||||
//总结
|
||||
//priority_queue,与STL相比支持了modify,erase和join
|
||||
//tree,相当于STL中的set/map,还支持split和join,运用tree_order_statistics_node_update还支持查询rank和k小值;更可以自定义Node_Update来维护更多信息
|
||||
//(目前比赛环境中)STL没有的两种hash_table
|
||||
//无脑用pb_ds代替std::set/map/priority_queue不会使程序变得更慢
|
Loading…
Reference in New Issue
Block a user