diff --git a/.ACM-Templates/Graph/求无向图的桥(get-bridge).cpp b/.ACM-Templates/Graph/求无向图的桥(get-bridge).cpp new file mode 100644 index 0000000..8978be2 --- /dev/null +++ b/.ACM-Templates/Graph/求无向图的桥(get-bridge).cpp @@ -0,0 +1,16 @@ +int dfs[MAXN], low[MAXN], tim = 0; +inline void Dfs(int u) +{ + dfs[u] = low[u] = ++tim; + for (Edge *p = a[u]; p; p = p->next) if (p->flag) { + if (!dfs[p->y]) { + p->opt->flag = false; + Dfs(p->y); + low[u] = min(low[u], low[p->y]); + } else low[u] = min(low[u], dfs[p->y]); + } + for (Edge *p = a[u]; p; p = p->next) if (p->opt->flag == false && low[p->y] > dfs[u]) { + p->bridge = p->opt->bridge = true; + } +} + diff --git a/.ACM-Templates/分数.cpp b/.ACM-Templates/分数.cpp new file mode 100644 index 0000000..380f704 --- /dev/null +++ b/.ACM-Templates/分数.cpp @@ -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; + } +}; diff --git a/.ACM-Templates/动态规划.cpp b/.ACM-Templates/动态规划.cpp new file mode 100644 index 0000000..a27a263 --- /dev/null +++ b/.ACM-Templates/动态规划.cpp @@ -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 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 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]; +} diff --git a/.ACM-Templates/博弈.cpp b/.ACM-Templates/博弈.cpp new file mode 100644 index 0000000..36627b7 --- /dev/null +++ b/.ACM-Templates/博弈.cpp @@ -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)的异或和 diff --git a/.ACM-Templates/图论-LCA.cpp b/.ACM-Templates/图论-LCA.cpp new file mode 100644 index 0000000..efa9c1e --- /dev/null +++ b/.ACM-Templates/图论-LCA.cpp @@ -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 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)]; } + } +} diff --git a/.ACM-Templates/图论-匹配.cpp b/.ACM-Templates/图论-匹配.cpp new file mode 100644 index 0000000..52571fb --- /dev/null +++ b/.ACM-Templates/图论-匹配.cpp @@ -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 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 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 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 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 e[N]; queue 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); } + } +} diff --git a/.ACM-Templates/图论-应用.cpp b/.ACM-Templates/图论-应用.cpp new file mode 100644 index 0000000..577a9ae --- /dev/null +++ b/.ACM-Templates/图论-应用.cpp @@ -0,0 +1,430 @@ +//拓扑排序 O(V^2) +//queue->普通判断用, priority_queue->字典序 +//邻接矩阵 +int n, mp[N][N], in[N], ret[N]; +int topoSort() { + queue 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 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, 连边, 容量为D[i] / 2; 对于每个D[j] < 0的点j, 连边, 容量为-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 q1, q2; +vector> dag; //缩点后的逆向DAG图 +char color[N]; //染色, 为'R'是选择的 +int cf[N], indeg[N]; //入度 +void solve(int n) { + dag.assign(scc + 1, vector()); + 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; +} diff --git a/.ACM-Templates/图论-最短路.cpp b/.ACM-Templates/图论-最短路.cpp new file mode 100644 index 0000000..efd50b0 --- /dev/null +++ b/.ACM-Templates/图论-最短路.cpp @@ -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 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 e[N]; wtype dist[N]; bool vis[N]; +void Dijkstra(int src) { + memset(dist, 0x3f, sizeof(dist)); + memset(vis, 0, sizeof(vis)); + priority_queue 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 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 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 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 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 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 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]}); + } + } +} diff --git a/.ACM-Templates/图论-生成树.cpp b/.ACM-Templates/图论-生成树.cpp new file mode 100644 index 0000000..e5084e5 --- /dev/null +++ b/.ACM-Templates/图论-生成树.cpp @@ -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 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]表示。 diff --git a/.ACM-Templates/图论-网络流.cpp b/.ACM-Templates/图论-网络流.cpp new file mode 100644 index 0000000..ccb396d --- /dev/null +++ b/.ACM-Templates/图论-网络流.cpp @@ -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 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 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 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 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> 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 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; +} diff --git a/.ACM-Templates/图论-连通性.cpp b/.ACM-Templates/图论-连通性.cpp new file mode 100644 index 0000000..440c858 --- /dev/null +++ b/.ACM-Templates/图论-连通性.cpp @@ -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) 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 e[N]; +int low[N], dfn[N], index, belong[N], num[N], scc; //所属分量, 分量点数, 分量个数 +stack 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 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 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 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次, 把所有点收缩到了一起 diff --git a/.ACM-Templates/字符串.cpp b/.ACM-Templates/字符串.cpp new file mode 100644 index 0000000..d2b6570 --- /dev/null +++ b/.ACM-Templates/字符串.cpp @@ -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 +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 struct hashmap { + vector 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 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 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; diff --git a/.ACM-Templates/并查集.cpp b/.ACM-Templates/并查集.cpp new file mode 100644 index 0000000..eeac50c --- /dev/null +++ b/.ACM-Templates/并查集.cpp @@ -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; } diff --git a/.ACM-Templates/应用.cpp b/.ACM-Templates/应用.cpp new file mode 100644 index 0000000..f544d52 --- /dev/null +++ b/.ACM-Templates/应用.cpp @@ -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 revCantor(ll n, ll k) { + vector 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 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 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" +}; diff --git a/.ACM-Templates/排序查找.cpp b/.ACM-Templates/排序查找.cpp new file mode 100644 index 0000000..9ce6b06 --- /dev/null +++ b/.ACM-Templates/排序查找.cpp @@ -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 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); +} diff --git a/.ACM-Templates/数学数论.cpp b/.ACM-Templates/数学数论.cpp new file mode 100644 index 0000000..c659c47 --- /dev/null +++ b/.ACM-Templates/数学数论.cpp @@ -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 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 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 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 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 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 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 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 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 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; + } + } + } +} diff --git a/.ACM-Templates/数据结构.cpp b/.ACM-Templates/数据结构.cpp new file mode 100644 index 0000000..ee33adb --- /dev/null +++ b/.ACM-Templates/数据结构.cpp @@ -0,0 +1,2209 @@ +//一维树状数组 +//单点修改 + 单点查询 + 区间修改 + 区间查询 + 区间最值 +int n; +template struct BIT { + T A[N]; //T B[N]; //区间增减/维护最值 + int lowbit(int x) { return x & -x; } + void init() { memset(A, 0, sizeof(A)); /*memset(B, 0, sizeof(B));*/ } + void update(int i, T v) { while (i <= n) { A[i] += v; i += lowbit(i); } } + T query(int i) { T ret = 0; while (i) { ret += A[i]; i -= lowbit(i); } return ret; } + T query(int i, int j) { return query(j) - query(i - 1); } + //区间修改 + T query(int x) { + if (!x) { return 0; } + T ret1 = 0, ret2 = 0; + for (int i = x; i <= n; i += lowbit(i)) { ret1 += A[i]; } + for (int i = x - 1; i > 0; i -= lowbit(i)) { ret2 += B[i]; } + return ret1 * x + ret2; + } + void update(int x, T v) { + for (int i = x; i > 0; i -= lowbit(i)) { A[i] += v; } + for (int i = x; i <= n; i += lowbit(i)) { B[i] += x * v; } + } + void update(int i, int j, T v) { update(j, v); if (i > 1) { update(i - 1, -v); } } + //维护区间最值 O(log^2(n)) + void modify(int x, T v) { + B[x] = v; + for (int i = x; i <= n; i += lowbit(i)) { + A[i] = max(A[i], v); + for (int j = 1; j < lowbit(i); j <<= 1) { + A[i] = max(A[i], A[i - j]); + } + } + } + T query(int l, int r) { + T ret = B[r]; + while (true) { + ret = max(ret, B[r]); + if (l == r) { break; } + for (r -= 1; r - l >= lowbit(r); r -= lowbit(r)) { ret = max(ret, A[r]); } + } + return ret; + } + //求区间第K大的下标/值 O(log^2(n)) + int getK(int l, int r, int k) { + while (l <= r) { + int mid = l + ((r - l) >> 1); + if (query(mid) >= k) { r = mid - 1; } + else { l = mid + 1; } + } + return l; //A[l] + } +}; +BIT bit; +//二维树状数组 +//单点修改 + 单点查询 + 区域修改 + 区域查询 +int n, m; +template struct BIT { + T A[N][N]; //T B[N][N], C[N][N], D[N][N]; //区域求和 + int lowbit(int x) { return x & -x; } + void init() { memset(A, 0, sizeof(A)); /*memset(B, 0, sizeof(B)); memset(C, 0, sizeof(C)); memset(D, 0, sizeof(D));*/ } + T get(int x, int y) { + T ret = 0; + for (int i = x; i > 0; i -= lowbit(i)) { for (int j = y; j > 0; j -= lowbit(j)) { ret += A[i][j]; } } + return ret; + } + T query(int x, int y) { return get(x, y) - get(x, y - 1) - get(x - 1, y) + get(x - 1, y - 1); } + void update(int x, int y, T v) { + for (int i = x; i <= n; i += lowbit(i)) { for (int j = y; j <= m; j += lowbit(j)) { A[i][j] += v; } } + } + //区域和[x1][y1]-[x2][y2] + T query(int x1, int y1, int x2, int y2) { + return get(x2, y2) - get(x1 - 1, y2) - get(x2, y1 - 1) + get(x1 - 1, y1 - 1); + } + //区域增减 + void update(int x, int y, T v, T a[][N]) { + for (int i = x; i <= n; i += lowbit(i)) { for (int j = y; j <= m; j += lowbit(j)) { a[i][j] += v; } } + } + void update(int x1, int y1, int x2, int y2, T v) { + update(x1, y1, v, A); update(x2 + 1, y1, -v, A); + update(x1, y2 + 1, -v, A); update(x2 + 1, y2 + 1, v, A); + update(x1, y1, v * x1, B); update(x2 + 1, y1, -v * (x2 + 1), B); + update(x1, y2 + 1, -v * x1, B); update(x2 + 1, y2 + 1, v * (x2 + 1), B); + update(x1, y1, v * y1, C); update(x2 + 1, y1, -v * y1, C); + update(x1, y2 + 1, -v * (y2 + 1), C); update(x2 + 1, y2 + 1, v * (y2 + 1), C); + update(x1, y1, v * x1 * y1, D); update(x2 + 1, y1, -v * (x2 + 1) * y1, D); + update(x1, y2 + 1, -v * x1 * (y2 + 1), D); update(x2 + 1, y2 + 1, v * (x2 + 1) * (y2 + 1), D); + } +}; +BIT bit; +//线段树 单点修改 + 区间查询 +#define lson l,m,rt<<1 +#define rson m+1,r,rt<<1|1 +template struct SegmentTree { + T data[N << 2]; + T calc(const T &x, const T &y)const { return x + y; } + void push_up(int rt) { data[rt] = calc(data[rt << 1], data[rt << 1 | 1]); } + void build(int l, int r, int rt) { + if (l == r) { scanf("%d", &data[rt]); return; } + int m = (l + r) >> 1; + build(lson); + build(rson); + push_up(rt); + } + void update(int p, T val, int l, int r, int rt) { + if (l == r) { data[rt] += val; return; } + int m = (l + r) >> 1; + if (p <= m) { update(p, val, lson); } + else { update(p, val, rson); } + push_up(rt); + } + T query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { return data[rt]; } + int m = (l + r) >> 1; T ret = 0; + if (L <= m) { ret = calc(ret, query(L, R, lson)); } + if (m < R) { ret = calc(ret, query(L, R, rson)); } + return ret; + } +}; +SegmentTree st; +//线段树 区间查询/修改 + 延迟标记 +#define lson l,m,rt<<1 +#define rson m+1,r,rt<<1|1 +template struct SegmentTree { + T data[N << 2], lazy[N << 2]; + T calc(const T &x, const T &y)const { return x + y; } + void push_up(int rt) { data[rt] = calc(data[rt << 1], data[rt << 1 | 1]); } + void push_down(int rt, int len) { + if (lazy[rt]) { + data[rt << 1] += lazy[rt] * (len - (len >> 1)); lazy[rt << 1] += lazy[rt]; + data[rt << 1 | 1] += lazy[rt] * (len >> 1); lazy[rt << 1 | 1] += lazy[rt]; + lazy[rt] = 0; + } + } + void build(int l, int r, int rt) { + lazy[rt] = 0; + if (l == r) { scanf("%d", &data[rt]); return; } + int m = (l + r) >> 1; + build(lson); + build(rson); + push_up(rt); + } + void update(int L, int R, T val, int l, int r, int rt) { + if (L <= l && r <= R) { + data[rt] += val * (r - l + 1); + lazy[rt] += val; + return; + } + push_down(rt, r - l + 1); + int m = (l + r) >> 1; + if (L <= m) { update(L, R, val, lson); } + if (m < R) { update(L, R, val, rson); } + push_up(rt); + } + T query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { return data[rt]; } + push_down(rt, r - l + 1); + int m = (l + r) >> 1; T ret = 0; + if (L <= m) { ret = calc(ret, query(L, R, lson)); } + if (m < R) { ret = calc(ret, query(L, R, rson)); } + return ret; + } +}; +SegmentTree st; +//非递归版线段树 单点修改 + 区间查询 +const int N = ((131072 << 1) + 10); //节点个数->不小于区间长度+2的最小2的正整数次幂*2+10 +#define l(x) ((x)<<1) //x的左儿子 +#define r(x) (((x)<<1)|1) //x的右儿子 +template struct zkwSegmentTree { + int m; //底层节点数 + T sum[N]; //区间和 + void build(int n) { + for (m = 1; m < n + 2; m <<= 1); + for (int i = 1; i <= n; i++) { scanf("%d", &sum[m + i]); } + for (int i = m - 1; i; i--) { sum[i] = sum[l(i)] + sum[r(i)]; } + } + void update(int p, T val) { + for (sum[p += m] += val, p >>= 1; p; p >>= 1) { + sum[p] = sum[l(p)] + sum[r(p)]; + } + } + T query(int l, int r) { + T ret = 0; + for (l += m - 1, r += m + 1; l ^ r ^ 1; l >>= 1, r >>= 1) { + if (~l & 1) { ret += sum[l ^ 1]; } + if (r & 1) { ret += sum[r ^ 1]; } + } + return ret; + } +}; +zkwSegmentTree st; +//非递归版线段树 区间查询/修改 + 延迟标记 +const int N = ((131072 << 1) + 10); //节点个数->不小于区间长度+2的最小2的正整数次幂*2+10 +#define l(x) ((x)<<1) //x的左儿子 +#define r(x) (((x)<<1)|1) //x的右儿子 +template struct zkwSegmentTree { + int m, h; //底层节点数 高度 + T sum[N], add[N]; //区间和 延迟标记 + void pushdown(int rt) { + for (int i = h, p; i; i--) { //自顶向下 + if (add[p = rt >> i]) { + add[p] >>= 1; //add[p]为节点增加总量, 子节点增加一半 + sum[l(p)] += add[p]; add[l(p)] += add[p]; + sum[r(p)] += add[p]; add[r(p)] += add[p]; + add[p] = 0; + } + } + } + void build(int n) { + for (m = 1, h = 0; m < n + 2; m <<= 1, h++); + for (int i = 1; i <= n; i++) { scanf("%d", &sum[m + i]); } + for (int i = m - 1; i; i--) { sum[i] = sum[l(i)] + sum[r(i)]; } + } + void update(int l, int r, T val) { + l += m - 1, r += m + 1; int ll = l >> 1, rr = r >> 1; + for (pushdown(l), pushdown(r); l ^ r ^ 1; l >>= 1, r >>= 1, val <<= 1) { + if (~l & 1) { sum[l ^ 1] += val; add[l ^ 1] += val; } + if (r & 1) { sum[r ^ 1] += val; add[r ^ 1] += val; } + } + for (; ll; ll >>= 1) { sum[ll] = sum[l(ll)] + sum[r(ll)]; } + for (; rr; rr >>= 1) { sum[rr] = sum[l(rr)] + sum[r(rr)]; } + } + T query(int l, int r) { + T ret = 0; l += m - 1, r += m + 1; + for (pushdown(l), pushdown(r); l ^ r ^ 1; l >>= 1, r >>= 1) { + if (~l & 1) { ret += sum[l ^ 1]; } + if (r & 1) { ret += sum[r ^ 1]; } + } + return ret; + } +}; +zkwSegmentTree st; +//可持久化线段树 +const int N = 100005; +const int M = 2500005; +const int INF = 0x3f3f3f3f; +int n, m, nn; //离散化后大小 +#define lson l,m,ls[rt] +#define rson m+1,r,rs[rt] +template struct SegmentTree { + int ls[M], rs[M], root[N], tot; T data[M]; + int new_node() { return ++tot; } + void build(int l, int r, int &rt) { + rt = new_node(); data[rt] = 0; + if (l == r) { return; } + int m = (l + r) >> 1; + build(lson); + build(rson); + } + void update(int p, T val, int lst, int l, int r, int &rt) { + rt = new_node(); ls[rt] = ls[lst]; rs[rt] = rs[lst]; data[rt] = data[lst] + val; + if (l == r) { return; } + int m = (l + r) >> 1; + if (p <= m) { update(p, val, ls[lst], lson); } + else { update(p, val, rs[lst], rson); } + } + //带修改区间第k小 + int tree[N], use[N]; + int lowbit(int x) { return x & -x; } + void modify(int x, int p, T val) { //x为原数列中的下标, p为值 + for (int i = x; i <= n; i += lowbit(i)) { update(p, val, tree[i], 1, nn, tree[i]); } + } + T query(int x) { + T ret = 0; + for (int i = x; i; i -= lowbit(i)) { ret += data[ls[use[i]]]; } + return ret; + } + int query(int L, int R, int k, int l, int r) { + for (int i = L; i; i -= lowbit(i)) { use[i] = tree[i]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = tree[i]; } + int lr = root[L], rr = root[R]; + while (l < r) { + int m = (l + r) >> 1; T tmp = query(R) - query(L) + data[ls[rr]] - data[ls[lr]]; + if (k <= tmp) { + r = m; + for (int i = L; i; i -= lowbit(i)) { use[i] = ls[use[i]]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = ls[use[i]]; } + lr = ls[lr]; rr = ls[rr]; + } else { + l = m + 1; k -= tmp; + for (int i = L; i; i -= lowbit(i)) { use[i] = rs[use[i]]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = rs[use[i]]; } + lr = rs[lr]; rr = rs[rr]; + } + } + return l; + } +}; +SegmentTree st; +//ZOJ 2112 +int a[N], hs[N], l[N], r[N], k[N]; +char op[N]; +int main() { + int C = 0, T; + scanf("%d", &T); + while (++C <= T) { + nn = 0; + scanf("%d%d", &n, &m); + for (int i = 1; i <= n; i++) { + scanf("%d", &a[i]); hs[++nn] = a[i]; + } + for (int i = 0; i < m; i++) { + scanf(" %c%d%d", &op[i], &l[i], &r[i]); + switch (op[i]) { + case 'Q': scanf("%d", &k[i]); break; + case 'C': hs[++nn] = r[i]; break; + } + } + sort(hs + 1, hs + nn + 1); + nn = unique(hs + 1, hs + nn + 1) - hs - 1; + for (int i = 1; i <= n; ++i) { + a[i] = lower_bound(hs + 1, hs + nn + 1, a[i]) - hs; + } + st.tot = 0; + st.build(1, nn, st.root[0]); + for (int i = 1; i <= n; i++) { + st.update(a[i], 1, st.root[i - 1], 1, nn, st.root[i]); + } + for (int i = 1; i <= n; i++) { st.tree[i] = st.root[0]; } + for (int i = 0; i < m; i++) { + switch (op[i]) { + case 'Q': + printf("%d\n", hs[st.query(l[i] - 1, r[i], k[i], 1, nn)]); + break; + case 'C': + st.modify(l[i], a[l[i]], -1); + a[l[i]] = lower_bound(hs + 1, hs + nn + 1, r[i]) - hs; + st.modify(l[i], a[l[i]], 1); + break; + } + } + } +} +//实时开节点的权值线段树 (无需离散化) O(logV) +const int N = 60005; +const int M = 2500005; +const int INF = 0x3f3f3f3f; +int n, a[N]; +#define lson l,m,ls[rt] +#define rson m+1,r,rs[rt] +struct SegmentTree { + int ls[M], rs[M], cnt[M], root[N], tot; + void init() { + tot = 0; memset(cnt, 0, sizeof(cnt)); memset(root, 0, sizeof(root)); + memset(ls, 0, sizeof(ls)); memset(rs, 0, sizeof(rs)); + } + int new_node() { return ++tot; } + void update(int p, int val, int l, int r, int &rt) { + if (!rt) { rt = new_node(); } + if (l == r) { cnt[rt] += val; return; } + int m = (l + r) >> 1; + if (p <= m) { update(p, val, lson); } + else { update(p, val, rson); } + cnt[rt] = cnt[ls[rt]] + cnt[rs[rt]]; + } + int use[N]; + int lowbit(int x) { return x & -x; } + //单点修改 + void modify(int x, int p, int val) { + for (int i = x; i <= n; i += lowbit(i)) { update(p, val, 0, INF, root[i]); } + } + int query(int x) { + int ret = 0; + for (int i = x; i; i -= lowbit(i)) { ret += cnt[ls[use[i]]]; } + return ret; + } + //查询区间第k小 + int query(int L, int R, int k, int l, int r) { + for (int i = L; i; i -= lowbit(i)) { use[i] = root[i]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = root[i]; } + while (l < r) { + int m = (l + r) >> 1, tmp = query(R) - query(L); + if (k <= tmp) { + r = m; + for (int i = L; i; i -= lowbit(i)) { use[i] = ls[use[i]]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = ls[use[i]]; } + } else { + l = m + 1; k -= tmp; + for (int i = L; i; i -= lowbit(i)) { use[i] = rs[use[i]]; } + for (int i = R; i; i -= lowbit(i)) { use[i] = rs[use[i]]; } + } + } + return l; + } +} st; +//BZOJ1901 区间第k小 +int main() { + int m, l, r, k; char op[5]; + while (~scanf("%d%d", &n, &m)) { + st.init(); + for (int i = 1; i <= n; ++i) { + scanf("%d", &a[i]); + st.modify(i, a[i], 1); + } + while (m--) { + scanf("%s%d%d", op, &l, &r); + switch (op[0]) { + case 'Q': + scanf("%d", &k); + printf("%d\n", st.query(l - 1, r, k, 0, INF)); + break; + case 'C': + st.modify(l, a[l], -1); + a[l] = r; + st.modify(l, a[l], 1); + break; + } + } + } +} +//平衡二叉树 常用操作 +//注意这些操作适用于不允许重复值的平衡二叉树(set而非multiset) +//对于允许重复值(拥有cnt域)的实现, 只要在一些+1的地方稍作修改(改成cnt[x])即可 +bool find(int v) { + for (int x = root; x; x = ch[x][key[x] < v]) { + if (key[x] == v) { return true; } + } + return false; +} +int getKth(int k) { + int x = root; + while (size[ch[x][0]] + 1 != k) { + if (k < size[ch[x][0]] + 1) { x = ch[x][0]; } + else { k -= size[ch[x][0]] + 1; x = ch[x][1]; } + } + return key[x]; +} +int getRank(int v) { + int ret = 0, x = root; + while (x) { + if (v < key[x]) { x = ch[x][0]; } + else { ret += size[ch[x][0]] + 1; x = ch[x][1]; } + } + return ret; +} +int getPre(int v) { + int x = root, y = 0; + while (x) { + if (v < key[x]) { x = ch[x][0]; } + else { y = x; x = ch[x][1]; } + } + return y; +} +int getNext(int v) { + int x = root, y = 0; + while (x) { + if (v > key[x]) { x = ch[x][1]; } + else { y = x; x = ch[x][0]; } + } + return y; +} +int getMin() { + if (size[root] == 0) { return -1; } + int x = root; + while (ch[x][0]) { x = ch[x][0]; } + return x; +} +int getMax() { + if (size[root] == 0) { return -1; } + int x = root; + while (ch[x][1]) { x = ch[x][1]; } + return x; +} +//Debug遍历 +void treaval(int x) { + if (x != 0) { + treaval(ch[x][0]); + printf("Node%2d:lson %2d rson %2d size = %2d ,val = %2d\n", x, ch[x][0], ch[x][1], size[x], key[x]); + treaval(ch[x][1]); + } +} +void debug() { + printf("root:%d\n", root); + treaval(root); + putchar('\n'); +} +//基于旋转的Treap 允许重复值 +//维护序列的有序性和堆的性质, 依靠堆值的随机化, +//将树的高度维护在期望下平衡的程度,从而实现了各种操作期望O(logn)的复杂度. +//它的性价比高在只有两种旋转(而且可以合并地写), 比红黑树和AVL短小 +struct Treap { + int tot, root; + int ch[N][2], key[N], pt[N], cnt[N], size[N]; + void init() { tot = root = 0; pt[0] = INF; } + void push_up(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; } + void new_node(int &x, int v) { + x = ++tot; + ch[x][0] = ch[x][1] = 0; + size[x] = cnt[x] = 1; + pt[x] = rand(); + key[x] = v; + } + void rotate(int &x, int f) { + int y = ch[x][f]; + ch[x][f] = ch[y][f ^ 1]; + ch[y][f ^ 1] = x; + push_up(x); + push_up(y); + x = y; + } + void insert(int &x, int v) { + if (!x) { new_node(x, v); return; } + if (key[x] == v) { + ++cnt[x]; + } else { + int f = key[x] < v; + insert(ch[x][f], v); + if (pt[ch[x][f]] < pt[x]) { + rotate(x, f); + } + } + push_up(x); + } + void erase(int &x, int v) { + if (!x) { return; } + if (key[x] == v) { + if (cnt[x] > 1) { + --cnt[x]; + } else { + if (!ch[x][0] && !ch[x][1]) { + x = 0; + } else { + rotate(x, pt[ch[x][0]] > pt[ch[x][1]]); + erase(x, v); + } + } + } else { + erase(ch[x][key[x] < v], v); + } + push_up(x); + } + void insert(int v) { insert(root, v); } + void erase(int v) { erase(root, v); } +} treap; +//Size Balanced Tree 不允许重复值 +//独特的平摊时间O(1)的Maintain操作, 具有仅次于红黑树的优秀的时间效率 +struct SBT { + int root, tot; + int ch[N][2], key[N], size[N]; + void init() { tot = root = 0; size[0] = 0; } + void rotate(int &x, int f) { + int y = ch[x][f]; + ch[x][f] = ch[y][f ^ 1]; + ch[y][f ^ 1] = x; + size[y] = size[x]; + size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; + x = y; + } + void maintain(int &x, int f) { + if (size[ch[ch[x][f]][f]] > size[ch[x][f ^ 1]]) { + rotate(x, f); + } else if (size[ch[ch[x][f]][f ^ 1]] > size[ch[x][f ^ 1]]) { + rotate(ch[x][f], f ^ 1); rotate(x, f); + } else { + return; + } + maintain(ch[x][0], 0); + maintain(ch[x][1], 1); + maintain(x, 0); + maintain(x, 1); + } + void insert(int &x, int v) { + if (!x) { + x = ++tot; + ch[x][0] = ch[x][1] = 0; + size[x] = 1; + key[x] = v; + } else { + ++size[x]; + insert(ch[x][key[x] < v], v); + maintain(x, key[x] < v); + } + } + int erase(int &x, int v) { + if (!x) { return 0; } + --size[x]; + if (key[x] == v || (key[x] > v && !ch[x][0]) || (key[x] < v && !ch[x][1])) { + int ret = key[x]; + if (ch[x][0] && ch[x][1]) { + key[x] = erase(ch[x][0], v + 1); + } else { + x = ch[x][0] + ch[x][1]; + } + return ret; + } + return erase(ch[x][key[x] < v], v); + } + void insert(int v) { insert(root, v); } + void erase(int v) { erase(root, v); } +} sbt; +//Splay +//可以实现很多其它平衡树无法实现的操作(如区间翻转), 既可以维护集合信息, +//也可以维护序列信息, 可以用它来做Treap的题, 也可以用它来做线段树的题. +//更重要的是, Splay可以实现split(将某棵子树从原树中分离)和merge操作(将某棵子树插入另一棵树), +//这也使得区间插入删除成为可能. 它的美中不足是常数稍大, 约是Treap的1.5~3倍, 线段树的2~5倍. +//Splay有单旋和双旋两种实现, 其中只有双旋保证了均摊O(logn)的单次操作复杂度, +//但因为很多人认为zigzag太长不好敲(大多是OI选手有此困扰), 选择了单旋. +//其实完全可以稍微损失一点常数, 合并成一个rotate函数来完成双旋. +//此外一个良好的实现通常要在序列一首一尾增加两个哨兵节点, 这样可以减少很多边界特判. +//有必要进行的扩展性说明是, 对于一棵树, 如果想要维护子树信息, 我们可以用Splay维护这棵树的括号序列(dfs序), +//这样便可以轻易split出任意子树所属的区间;而用Splay维护dfs序的结构, 就是Euler-Tour Tree. +//同样的, 如果想要维护链上信息, 可以先树链剖分然后用Splay维护每条重链, +//根据杨哲在07年国家集训队作业的计算, 因其势能分析得到的复杂度依然是单次操作均摊O(logn)复杂度; +//而类似的思想做些转化, 就变成了后面会提到的Link-Cut Tree(以下简称LCT). +//ver.1 +#define keyTree (ch[ch[root][1]][0]) +const int N = 200005; +const int INF = 0x3f3f3f3f; +int num[N]; +struct Splay { + int root, tot1, tot2; + int ch[N][2], pre[N], size[N]; + int gc[N], que[N]; + int key[N], vmin[N], add[N], rev[N]; + void rotate(int x, int f) { + int y = pre[x]; + ch[y][f ^ 1] = ch[x][f]; + pre[ch[x][f]] = y; + pre[x] = pre[y]; + if (pre[x]) { + ch[pre[y]][ch[pre[y]][1] == y] = x; + } + ch[x][f] = y; + pre[y] = x; + push_up(y); + } + void splay(int x, int goal) { + push_down(x); + while (pre[x] != goal) { + int y = pre[x], z = pre[y]; + if (z == goal) { + push_down(y); + push_down(x); + rotate(x, ch[y][0] == x); + } else { + push_down(z); + push_down(y); + push_down(x); + int f = ch[z][0] == y; + if (ch[y][f] == x) { + rotate(x, f ^ 1); + } else { + rotate(y, f); + } + rotate(x, f); + } + } + push_up(x); + if (goal == 0) { + root = x; + } + } + void rotate_to(int k, int goal) { + int x = root; + push_down(x); + while (size[ch[x][0]] != k) { + if (k < size[ch[x][0]]) { + x = ch[x][0]; + } else { + k -= size[ch[x][0]] + 1; + x = ch[x][1]; + } + push_down(x); + } + splay(x, goal); + } + void erase(int x) { + int fa = pre[x], head = 0, tail = 0; + for (que[tail++] = x; head < tail; ++head) { + gc[tot2++] = que[head]; + if (ch[que[head]][0]) { + que[tail++] = ch[que[head]][0]; + } + if (ch[que[head]][1]) { + que[tail++] = ch[que[head]][1]; + } + } + ch[fa][ch[fa][1] == x] = 0; + push_up(fa); + } + void new_node(int &x, int v, int fa) { + if (tot2) { + x = gc[--tot2]; + } else { + x = ++tot1; + } + ch[x][0] = ch[x][1] = 0; + pre[x] = fa; + size[x] = 1; + key[x] = vmin[x] = v; + add[x] = rev[x] = 0; + } + void update_add(int x, int d) { + if (x) { + key[x] += d; + add[x] += d; + vmin[x] += d; + } + } + void update_rev(int x) { + if (x) { + swap(ch[x][0], ch[x][1]); + rev[x] ^= 1; + } + } + void push_up(int x) { + size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; + vmin[x] = min(key[x], min(vmin[ch[x][0]], vmin[ch[x][1]])); + } + void push_down(int x) { + if (add[x]) { + update_add(ch[x][0], add[x]); + update_add(ch[x][1], add[x]); + add[x] = 0; + } + if (rev[x]) { + update_rev(ch[x][0]); + update_rev(ch[x][1]); + rev[x] = 0; + } + } + void build(int &x, int l, int r, int f) { + int m = l + r >> 1; + new_node(x, num[m], f); + if (l < m) { + build(ch[x][0], l, m - 1, x); + } + if (r > m) { + build(ch[x][1], m + 1, r, x); + } + push_up(x); + } + void init(int n) { + root = tot1 = tot2 = 0; + ch[0][0] = ch[0][1] = pre[0] = size[0] = 0; + add[0] = rev[0] = 0; + key[0] = vmin[0] = INF; + new_node(root, -1, 0); + new_node(ch[root][1], -1, root); + size[root] = 2; + for (int i = 1; i <= n; ++i) { + scanf("%d", &num[i]); + } + build(keyTree, 1, n, ch[root][1]); + push_up(ch[root][1]); + push_up(root); + } + void plus(int l, int r, int v) { + rotate_to(l - 1, 0); + rotate_to(r + 1, root); + update_add(keyTree, v); + } + void reverse(int l, int r) { + rotate_to(l - 1, 0); + rotate_to(r + 1, root); + update_rev(keyTree); + } + void revolve(int l, int r, int k) { + k %= r - l + 1; + if (!k) { + return; + } + rotate_to(r - k, 0); + rotate_to(r + 1, root); + int tmp = keyTree; + keyTree = 0; + push_up(ch[root][1]); + push_up(root); + rotate_to(l - 1, 0); + rotate_to(l, root); + keyTree = tmp; + pre[tmp] = ch[root][1]; + push_up(ch[root][1]); + push_up(root); + } + void insert(int k, int v) { + rotate_to(k, 0); + rotate_to(k + 1, root); + new_node(keyTree, v, ch[root][1]); + push_up(ch[root][1]); + push_up(root); + } + void del(int k) { + rotate_to(k - 1, 0); + rotate_to(k + 1, root); + erase(keyTree); + push_up(ch[root][1]); + push_up(root); + } + int query(int l, int r) { + rotate_to(l - 1, 0); + rotate_to(r + 1, root); + return vmin[keyTree]; + } +} splay; +int main() { + int n, m, x, y, v; + char op[10]; + while (~scanf("%d", &n)) { + splay.init(n); + scanf("%d", &m); + while (m--) { + scanf("%s", op); + switch (op[0]) { + case 'A': + scanf("%d%d%d", &x, &y, &v); + splay.plus(x, y, v); + break; + case 'R': + scanf("%d%d", &x, &y); + if (op[3] == 'E') { + splay.reverse(x, y); + } else { + scanf("%d", &v); + splay.revolve(x, y, v); + } + break; + case 'I': + scanf("%d%d", &x, &v); + splay.insert(x, v); + break; + case 'D': + scanf("%d", &x); + splay.del(x); + break; + case 'M': + scanf("%d%d", &x, &y); + printf("%d\n", splay.query(x, y)); + break; + } + } + } +} +//ver.2 +int k1, k2, num[N]; +struct Splay { + int root, tot, point; + int ch[N][2], pre[N], size[N]; + int key[N], add[N], rev[N]; + bool isroot(int x) { return !pre[x] || ch[pre[x]][0] != x && ch[pre[x]][1] != x; } + void rotate(int x) { + int y = pre[x], f = ch[y][1] == x; + ch[y][f] = ch[x][f ^ 1]; + pre[ch[y][f]] = y; + if (!isroot(y)) { ch[pre[y]][ch[pre[y]][1] == y] = x; } + pre[x] = pre[y]; + ch[x][f ^ 1] = y; + pre[y] = x; + push_up(y); + } + void splay(int x) { + push_down(x); + while (!isroot(x)) { + int y = pre[x], z = pre[y]; + if (isroot(y)) { + push_down(y); + push_down(x); + rotate(x); + } else { + push_down(z); + push_down(y); + push_down(x); + rotate((ch[z][1] == y) == (ch[y][1] == x) ? y : x); + rotate(x); + } + } + push_up(x); + } + void new_node(int &x, int v, int fa) { + x = ++tot; + ch[x][0] = ch[x][1] = 0; + pre[x] = fa; + size[x] = 1; + key[x] = v; + add[x] = rev[x] = 0; + } + void update_add(int x, int v) { + if (x) { key[x] += v; add[x] += v; } + } + void update_rev(int x) { + if (x) { rev[x] ^= 1; swap(ch[x][0], ch[x][1]); } + } + void push_down(int x) { + if (add[x]) { + update_add(ch[x][0], add[x]); + update_add(ch[x][1], add[x]); + add[x] = 0; + } + if (rev[x]) { + update_rev(ch[x][0]); + update_rev(ch[x][1]); + rev[x] = 0; + } + } + void push_up(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; } + void build(int &x, int l, int r, int fa) { + int m = l + r >> 1; + new_node(x, num[m], fa); + if (l < m) { build(ch[x][0], l, m - 1, x); } + if (r > m) { build(ch[x][1], m + 1, r, x); } + push_up(x); + } + void init(int n) { + root = tot = size[0] = 0; + for (int i = 1; i <= n; ++i) { + scanf("%d", &num[i]); + } + build(root, 1, n, 0); + point = 1; + } + int find(int rt, int k) { + int x = rt; + while (size[ch[x][0]] + 1 != k) { + push_down(x); + if (k <= size[ch[x][0]]) { + x = ch[x][0]; + } else { + k -= size[ch[x][0]] + 1; + x = ch[x][1]; + } + } + return x; + } + void split(int &x, int &y, int sz) { + if (!sz) { y = x; x = 0; return; } + y = find(x, sz + 1); + splay(y); + x = ch[y][0]; + ch[y][0] = 0; + push_up(y); + } + void split3(int &x, int &y, int &z, int l, int r) { + split(x, z, r); split(x, y, l - 1); + } + void join(int &x, int &y) { + if (!x || !y) { x |= y; return; } + x = find(x, size[x]); + splay(x); + ch[x][1] = y; + pre[y] = x; + push_up(x); + } + void join3(int &x, int y, int z) { + join(y, z); join(x, y); + } + void evert() { + if (point > 1) { + int x; + split(root, x, point - 1); + swap(root, x); + join(root, x); + point = 1; + } + } + void plus(int v) { + evert(); + int x, y; + split3(root, x, y, point, point + k2 - 1); + update_add(x, v); + join3(root, x, y); + } + void reverse() { + evert(); + int x, y; + split3(root, x, y, point, point + k1 - 1); + update_rev(x); + join3(root, x, y); + } + void insert(int v) { + evert(); + int x, y; + split(root, x, point); + new_node(y, v, 0); + join3(root, y, x); + } + void erase() { + evert(); + int x, y; + split3(root, x, y, point, point); + join(root, y); + } + void move(int tag) { + switch (tag) { + case 1: + if (--point == 0) { point = size[root]; } + break; + case 2: + if (++point == size[root] + 1) { point = 1; } + break; + } + } + void query() { + evert(); + int x, y; + split3(root, x, y, point, point); + printf("%d\n", key[x]); + join3(root, x, y); + } +} splay; +//HDU4453 +int main() { + int n, m, v, cas = 0; + char op[10]; + while (~scanf("%d%d%d%d", &n, &m, &k1, &k2) && (n || m || k1 || k2)) { + splay.init(n); + printf("Case #%d:\n", ++cas); + while (m--) { + scanf("%s", op); + switch (op[0]) { + case 'a': + scanf("%d", &v); + splay.plus(v); + break; + case 'r': + splay.reverse(); + break; + case 'i': + scanf("%d", &v); + splay.insert(v); + break; + case 'd': + splay.erase(); + break; + case 'm': + scanf("%d", &v); + splay.move(v); + break; + case 'q': + splay.query(); + break; + } + } + } +} +//ver.3 +const int N = 500005; +const int INF = 0x3f3f3f3f; +int n, q; +struct Splay { + int pre[N], ch[N][2], key[N], size[N]; + int root, tot1; + int sum[N], rev[N], same[N]; + int lx[N], rx[N], mx[N]; + int s[N], tot2; //内存池和容量 + int a[N]; + void NewNode(int &r, int father, int k) { + if (tot2) { r = s[tot2--]; } //取的时候是tot2--,存的时候就是++tot2 + else { r = ++tot1; } + pre[r] = father; + ch[r][0] = ch[r][1] = 0; + key[r] = k; + sum[r] = k; + rev[r] = same[r] = 0; + lx[r] = rx[r] = mx[r] = k; + size[r] = 1; + } + void Update_Rev(int r) { + if (!r) { return; } + swap(ch[r][0], ch[r][1]); + swap(lx[r], rx[r]); + rev[r] ^= 1; + } + void Update_Same(int r, int v) { + if (!r) { return; } + key[r] = v; + sum[r] = v * size[r]; + lx[r] = rx[r] = mx[r] = max(v, v * size[r]); + same[r] = 1; + } + void push_up(int r) { + int lson = ch[r][0], rson = ch[r][1]; + size[r] = size[lson] + size[rson] + 1; + sum[r] = sum[lson] + sum[rson] + key[r]; + lx[r] = max(lx[lson], sum[lson] + key[r] + max(0, lx[rson])); + rx[r] = max(rx[rson], sum[rson] + key[r] + max(0, rx[lson])); + mx[r] = max(0, rx[lson]) + key[r] + max(0, lx[rson]); + mx[r] = max(mx[r], max(mx[lson], mx[rson])); + } + void push_down(int r) { + if (same[r]) { + Update_Same(ch[r][0], key[r]); + Update_Same(ch[r][1], key[r]); + same[r] = 0; + } + if (rev[r]) { + Update_Rev(ch[r][0]); + Update_Rev(ch[r][1]); + rev[r] = 0; + } + } + void Build(int &x, int l, int r, int father) { + if (l > r) { return; } + int mid = (l + r) / 2; + NewNode(x, father, a[mid]); + Build(ch[x][0], l, mid - 1, x); + Build(ch[x][1], mid + 1, r, x); + push_up(x); + } + void Init() { + root = tot1 = tot2 = 0; + ch[root][0] = ch[root][1] = size[root] = pre[root] = 0; + same[root] = rev[root] = sum[root] = key[root] = 0; + lx[root] = rx[root] = mx[root] = -INF; + NewNode(root, 0, -1); + NewNode(ch[root][1], root, -1); + for (int i = 0; i < n; i++) { + scanf("%d", &a[i]); + } + Build(ch[ch[root][1]][0], 0, n - 1, ch[root][1]); + push_up(ch[root][1]); + push_up(root); + } + //旋转,0为左旋, 1为右旋 + void Rotate(int x, int kind) { + int y = pre[x]; + push_down(y); + push_down(x); + ch[y][!kind] = ch[x][kind]; + pre[ch[x][kind]] = y; + if (pre[y]) { ch[pre[y]][ch[pre[y]][1] == y] = x; } + pre[x] = pre[y]; + ch[x][kind] = y; + pre[y] = x; + push_up(y); + } + //Splay调整, 将r结点调整到goal下面 + void Splay(int r, int goal) { + push_down(r); + while (pre[r] != goal) { + if (pre[pre[r]] == goal) { + push_down(pre[r]); + push_down(r); + Rotate(r, ch[pre[r]][0] == r); + } else { + push_down(pre[pre[r]]); + push_down(pre[r]); + push_down(r); + int y = pre[r]; + int kind = ch[pre[y]][0] == y; + if (ch[y][kind] == r) { + Rotate(r, !kind); + Rotate(r, kind); + } else { + Rotate(y, kind); + Rotate(r, kind); + } + } + } + push_up(r); + if (goal == 0) { root = r; } + } + int Get_kth(int r, int k) { + push_down(r); + int t = size[ch[r][0]] + 1; + if (t == k) { return r; } + if (t > k) { return Get_kth(ch[r][0], k); } + else { return Get_kth(ch[r][1], k - t); } + } + //在第pos个数后面插入tot个数 + void Insert(int pos, int tot) { + for (int i = 0; i < tot; i++) { scanf("%d", &a[i]); } + Splay(Get_kth(root, pos + 1), 0); + Splay(Get_kth(root, pos + 2), root); + Build(ch[ch[root][1]][0], 0, tot - 1, ch[root][1]); + push_up(ch[root][1]); + push_up(root); + } + //删除子树 + void erase(int r) { + if (!r) { return; } + s[++tot2] = r; + erase(ch[r][0]); + erase(ch[r][1]); + } + //从第pos个数开始连续删除tot个数 + void Delete(int pos, int tot) { + Splay(Get_kth(root, pos), 0); + Splay(Get_kth(root, pos + tot + 1), root); + erase(ch[ch[root][1]][0]); + pre[ch[ch[root][1]][0]] = 0; + ch[ch[root][1]][0] = 0; + push_up(ch[root][1]); + push_up(root); + } + //将从第pos个数开始的连续的tot个数修改为c + void Make_Same(int pos, int tot, int c) { + Splay(Get_kth(root, pos), 0); + Splay(Get_kth(root, pos + tot + 1), root); + Update_Same(ch[ch[root][1]][0], c); + push_up(ch[root][1]); + push_up(root); + } + //将第pos个数开始的连续tot个数进行反转 + void Reverse(int pos, int tot) { + Splay(Get_kth(root, pos), 0); + Splay(Get_kth(root, pos + tot + 1), root); + Update_Rev(ch[ch[root][1]][0]); + push_up(ch[root][1]); + push_up(root); + } + //得到第pos个数开始的tot个数的和 + int Get_Sum(int pos, int tot) { + Splay(Get_kth(root, pos), 0); + Splay(Get_kth(root, pos + tot + 1), root); + return sum[ch[ch[root][1]][0]]; + } + //得到第pos个数开始的tot个数中最大的子段和 + int Get_MaxSum(int pos, int tot) { + Splay(Get_kth(root, pos), 0); + Splay(Get_kth(root, pos + tot + 1), root); + return mx[ch[ch[root][1]][0]]; + } + void InOrder(int r) { + if (!r) { return; } + push_down(r); + InOrder(ch[r][0]); + printf("%d ", key[r]); + InOrder(ch[r][1]); + } +} splay; +int main() { + while (scanf("%d%d", &n, &q) == 2) { + splay.Init(); + char op[20]; + int x, y, z; + while (q--) { + scanf("%s", op); + if (strcmp(op, "INSERT") == 0) { + scanf("%d%d", &x, &y); + splay.Insert(x, y); + } else if (strcmp(op, "DELETE") == 0) { + scanf("%d%d", &x, &y); + splay.Delete(x, y); + } else if (strcmp(op, "MAKE-SAME") == 0) { + scanf("%d%d%d", &x, &y, &z); + splay.Make_Same(x, y, z); + } else if (strcmp(op, "REVERSE") == 0) { + scanf("%d%d", &x, &y); + splay.Reverse(x, y); + } else if (strcmp(op, "GET-SUM") == 0) { + scanf("%d%d", &x, &y); + printf("%d\n", splay.Get_Sum(x, y)); + } else if (strcmp(op, "MAX-SUM") == 0) { + printf("%d\n", splay.Get_MaxSum(1, splay.size[root] - 2)); + } + } + } +} +//Link-Cut Tree 动态树 +//维护多棵树(森林)的形态, 并在O(logn)的时间复杂度内维护链上信息; 但LCT处理子树信息将会非常麻烦. +//它的核心操作是access函数, 可以把某个节点到根的路径上所有点按照深度用Splay维护起来, +//从而结合evert函数(换跟操作)和splay操作可以实现对链的信息维护. +//LCT几乎可以实现除维护子树信息外以上的所有操作, 同时有着优越的理论复杂度, +//但实际常数较大, 很多不改变树形态的题用O(logn)的LCT并不比O(log^2n)的树链剖分套线段树更优越 +struct LCT { + int ch[N][2], pre[N], key[N], rev[N]; + int add[N], vmax[N]; + bool isroot(int x) { return !pre[x] || ch[pre[x]][0] != x && ch[pre[x]][1] != x; } + void rotate(int x) { + int y = pre[x], f = ch[y][1] == x; + ch[y][f] = ch[x][f ^ 1]; + pre[ch[y][f]] = y; + if (!isroot(y)) { ch[pre[y]][ch[pre[y]][1] == y] = x; } + pre[x] = pre[y]; + ch[x][f ^ 1] = y; + pre[y] = x; + push_up(y); + } + void splay(int x) { + push_down(x); + while (!isroot(x)) { + int y = pre[x], z = pre[y]; + if (isroot(y)) { + push_down(y); + push_down(x); + rotate(x); + } else { + push_down(z); + push_down(y); + push_down(x); + rotate((ch[z][1] == y) == (ch[y][1] == x) ? y : x); + rotate(x); + } + } + push_up(x); + } + int access(int x) { + int y = 0; + for (; x; x = pre[x]) { + splay(x); + ch[x][1] = y; + push_up(x); + y = x; + } + return y; + } + void evert(int x) { + rev[access(x)] ^= 1; + splay(x); + } + void push_up(int x) { vmax[x] = max(max(vmax[ch[x][0]], vmax[ch[x][1]]), key[x]); } + void push_down(int x) { + if (add[x]) { + key[x] += add[x]; + if (ch[x][0]) { + add[ch[x][0]] += add[x]; + vmax[ch[x][0]] += add[x]; + } + if (ch[x][1]) { + add[ch[x][1]] += add[x]; + vmax[ch[x][1]] += add[x]; + } + add[x] = 0; + } + if (rev[x]) { + if (ch[x][0]) { rev[ch[x][0]] ^= 1; } + if (ch[x][1]) { rev[ch[x][1]] ^= 1; } + swap(ch[x][0], ch[x][1]); + rev[x] = 0; + } + } + int find_root(int x) { + while (pre[x]) { x = pre[x]; } + return x; + } + //如果u,v不在同一颗子树中,则通过在u,v之间连边的方式,连接这两颗子树 + void link(int u, int v) { + if (find_root(u) == find_root(v)) { puts("-1"); return; } + evert(u); + pre[u] = v; + } + //如果u,v在同一颗子树中,且u!=v,则将u视为这颗子树的根以后,切断v与其父亲结点的连接 + void cut(int u, int v) { + if (u == v || find_root(u) != find_root(v)) { puts("-1"); return; } + evert(u); + access(v); + splay(v); + pre[ch[v][0]] = 0; + ch[v][0] = 0; + push_up(v); + } + //如果u,v在同一颗子树中,则将u,v之间路径上所有点的点权增加w + void update(int u, int v, int w) { + if (find_root(u) != find_root(v)) { puts("-1"); return; } + evert(u); + access(v); + splay(v); + add[v] += w; + vmax[v] += w; + push_down(v); + } + //如果u,v在同一颗子树中, 返回u,v之间路径上点权的最大值 + void query(int u, int v) { + if (find_root(u) != find_root(v)) { puts("-1"); return; } + evert(u); + access(v); + splay(v); + printf("%d\n", vmax[v]); + } + struct graph { + int head[N], to[N << 1], next[N << 1]; + int tot; + void init() { tot = 0; memset(head, 0xff, sizeof(head)); } + void add(int u, int v) { + to[tot] = v; + next[tot] = head[u]; + head[u] = tot++; + } + } g; + void dfs(int u, int fa) { + for (int i = g.head[u]; ~i; i = g.next[i]) { + int v = g.to[i]; + if (v != fa) { + dfs(v, u); + pre[v] = u; + } + } + } + void init(int n) { + int m, x, y; + g.init(); + for (int i = 1; i < n; ++i) { + scanf("%d%d", &x, &y); + g.add(x, y); g.add(y, x); + } + memset(ch, 0, sizeof(ch)); + memset(pre, 0, sizeof(pre)); + memset(rev, 0, sizeof(rev)); + memset(add, 0, sizeof(add)); + vmax[0] = 0; + for (int i = 1; i <= n; ++i) { + scanf("%d", &key[i]); + vmax[i] = key[i]; + } + dfs(1, 0); + } +} lct; +//HDU4010 +int main() { + int n, q, op, x, y, w; + while (~scanf("%d", &n)) { + lct.init(n); + scanf("%d", &q); + while (q--) { + scanf("%d", &op); + switch (op) { + case 1: + scanf("%d%d", &x, &y); + lct.link(x, y); + break; + case 2: + scanf("%d%d", &x, &y); + lct.cut(x, y); + break; + case 3: + scanf("%d%d%d", &w, &x, &y); + lct.update(x, y, w); + break; + case 4: + scanf("%d%d", &x, &y); + lct.query(x, y); + break; + } + } + putchar('\n'); + } +} +//不基于旋转的Treap +int num[N]; +struct Treap { + int tot, root; + int ch[N][2], pt[N], size[N]; + int key[N], vmin[N], add[N], rev[N]; + void init() { tot = 0; } + void new_node(int &x, int v) { + x = ++tot; + ch[x][0] = ch[x][1] = 0; + size[x] = 1; + pt[x] = rand(); + key[x] = vmin[x] = v; + add[x] = rev[x] = 0; + } + void merge(int &p, int x, int y) { + if (!x || !y) { p = x | y; return; } + if (pt[x] < pt[y]) { + push_down(x); + merge(ch[x][1], ch[x][1], y); + p = x; + } else { + push_down(y); + merge(ch[y][0], x, ch[y][0]); + p = y; + } + push_up(p); + } + void split(int p, int sz, int &x, int &y) { + if (!sz) { x = 0; y = p; return; } + push_down(p); + if (size[ch[p][0]] >= sz) { + y = p; + split(ch[p][0], sz, x, ch[y][0]); + } else { + x = p; + split(ch[p][1], sz - size[ch[p][0]] - 1, ch[x][1], y); + } + push_up(p); + } + void update_add(int x, int v) { + if (x) { key[x] += v; add[x] += v; vmin[x] += v; } + } + void update_rev(int x) { + if (x) { swap(ch[x][0], ch[x][1]); rev[x] ^= 1; } + } + void push_down(int x) { + if (add[x]) { + update_add(ch[x][0], add[x]); + update_add(ch[x][1], add[x]); + add[x] = 0; + } + if (rev[x]) { + update_rev(ch[x][0]); + update_rev(ch[x][1]); + rev[x] = 0; + } + } + void push_up(int x) { + size[x] = 1; + vmin[x] = key[x]; + if (ch[x][0]) { + size[x] += size[ch[x][0]]; + vmin[x] = min(vmin[x], vmin[ch[x][0]]); + } + if (ch[x][1]) { + size[x] += size[ch[x][1]]; + vmin[x] = min(vmin[x], vmin[ch[x][1]]); + } + } + int build(int &x, int l, int r) { + int m = l + r >> 1; + new_node(x, num[m]); + if (l < m) { build(ch[x][0], l, m - 1); } + if (r > m) { build(ch[x][1], m + 1, r); } + push_up(x); + } + void plus(int l, int r, int v) { + int x, y; + split(root, l - 1, root, x); + split(x, r - l + 1, x, y); + update_add(x, v); + merge(x, x, y); + merge(root, root, x); + } + void reverse(int l, int r) { + int x, y; + split(root, l - 1, root, x); + split(x, r - l + 1, x, y); + update_rev(x); + merge(x, x, y); + merge(root, root, x); + } + void revolve(int l, int r, int k) { + int x, y, p, q; + k %= r - l + 1; + if (!k) { return; } + split(root, l - 1, root, x); + split(x, r - l + 1, x, y); + split(x, r - l + 1 - k, p, q); + merge(x, q, p); + merge(x, x, y); + merge(root, root, x); + } + void insert(int k, int v) { + int x, y; + new_node(x, v); + split(root, k, root, y); + merge(root, root, x); + merge(root, root, y); + } + void erase(int k) { + int x, y; + split(root, k - 1, root, x); + split(x, 1, x, y); + merge(root, root, y); + } + int query(int l, int r) { + int x, y, ret; + split(root, l - 1, root, x); + split(x, r - l + 1, x, y); + ret = vmin[x]; + merge(x, x, y); + merge(root, root, x); + return ret; + } +} treap; +//POJ3580 +int main() { + int n, m, x, y, v; + char op[10]; + while (~scanf("%d", &n)) { + treap.init(); + for (int i = 1; i <= n; ++i) { + scanf("%d", &num[i]); + } + treap.build(treap.root, 1, n); + scanf("%d", &m); + while (m--) { + scanf("%s", op); + switch (op[0]) { + case 'A': + scanf("%d%d%d", &x, &y, &v); + treap.plus(x, y, v); + break; + case 'R': + scanf("%d%d", &x, &y); + if (op[3] == 'E') { + treap.reverse(x, y); + } else { + scanf("%d", &v); + treap.revolve(x, y, v); + } + break; + case 'I': + scanf("%d%d", &x, &v); + treap.insert(x, v); + break; + case 'D': + scanf("%d", &x); + treap.erase(x); + break; + case 'M': + scanf("%d%d", &x, &y); + printf("%d\n", treap.query(x, y)); + break; + } + } + } +} +//可持久化Treap +const int N = 50005; +const int M = 5000005; +int root[N], vs, d; +struct Treap { + int tot; + int ch[M][2], size[M]; + char key[M]; + bool hey(int x, int y) { return (ll)rand() * (size[x] + size[y]) < (ll)size[x] * RAND_MAX; } + void init() { tot = 0; } + void new_node(int &x, char v) { + x = ++tot; + ch[x][0] = ch[x][1] = 0; + size[x] = 1; + key[x] = v; + } + void copy_node(int &x, int y) { + if (!y) { x = 0; return; } + x = ++tot; + ch[x][0] = ch[y][0]; + ch[x][1] = ch[y][1]; + size[x] = size[y]; + key[x] = key[y]; + } + void merge(int &p, int x, int y) { + if (!x || !y) { + p = 0; + if (x) { copy_node(p, x); } + if (y) { copy_node(p, y); } + return; + } + if (hey(x, y)) { + copy_node(p, x); + merge(ch[p][1], ch[x][1], y); + } else { + copy_node(p, y); + merge(ch[p][0], x, ch[y][0]); + } + push_up(p); + } + void split(int p, int sz, int &x, int &y) { + if (!sz) { x = 0; copy_node(y, p); return; } + if (size[ch[p][0]] >= sz) { + copy_node(y, p); + split(ch[p][0], sz, x, ch[y][0]); + push_up(y); + } else { + copy_node(x, p); + split(ch[p][1], sz - size[ch[p][0]] - 1, ch[x][1], y); + push_up(x); + } + } + void push_up(int x) { + size[x] = 1; + if (ch[x][0]) { size[x] += size[ch[x][0]]; } + if (ch[x][1]) { size[x] += size[ch[x][1]]; } + } + void build(char str[], int &x, int l, int r) { + int m = l + r >> 1; + new_node(x, str[m]); + if (l < m) { build(str, ch[x][0], l, m - 1); } + if (r > m) { build(str, ch[x][1], m + 1, r); } + push_up(x); + } + void insert(int k, char str[]) { + int x, y, z; + build(str, x, 0, strlen(str) - 1); + split(root[vs], k, y, z); + merge(y, y, x); + merge(root[++vs], y, z); + } + void erase(int k, int sz) { + int x, y, z; + split(root[vs], k - 1, x, y); + split(y, sz, y, z); + merge(root[++vs], x, z); + } + void output(int x) { + if (ch[x][0]) { output(ch[x][0]); } + putchar(key[x]); + d += key[x] == 'c'; + if (ch[x][1]) { output(ch[x][1]); } + } + void output(int v, int k, int sz) { + int x, y, z; + split(root[v], k - 1, x, y); + split(y, sz, y, z); + output(y); + putchar('\n'); + } +} treap; +//UVa12538 +int main() { + int n, op, p, c, v; + char s[105]; + treap.init(); + vs = d = 0; + scanf("%d", &n); + while (n--) { + scanf("%d", &op); + switch (op) { + case 1: + scanf("%d%s", &p, s); + treap.insert(p - d, s); + break; + case 2: + scanf("%d%d", &p, &c); + treap.erase(p - d, c - d); + break; + case 3: + scanf("%d%d%d", &v, &p, &c); + treap.output(v - d, p - d, c - d); + break; + } + } +} +//树链剖分 +//轻重链剖分将一棵树划分成至多logn条重链和若干条轻边, 满足每个节点属于一条重链, +//从而将树上路径修改转化为至多logn次线性修改, 非常利于套用树状数组、线段树等各类数据结构. +//树链剖分的常数很小, 且因着树链剖分的性质, 我们发现越是退化的树(极端情况下成为一条链), +//树链剖分的效果越是好(极端情况下甚至是O(1)级的, 因为只有很少的重链), +//以至于一些不涉及形态修改的树上路径维护题目, 可以用树链剖分套线段树以O(logn^2)的单次操作复杂度水过, +//且实际表现不输于单次操作O(logn)但常数很大的LCT. +//常见轻重链剖分的初始化实现是两次dfs的, 但dfs有两个问题, +//一是递归调用使得时间稍慢, 二是有些题目有爆栈风险; 所以我抄了bfs实现的很好用的交大板. +//需要稍作说明的是, 对于点权修改直接维护即可, 对于边权修改, 常规做法是选定一个根, +//将边权下垂到深度更大的节点上; 换言之, 每个点储存的权值是它与它的父节点之间的边权, 根节点上没有权值. +int top[N]; //top[p]表示编号为p的路径的顶端节点 +int len[N]; //len[p]表示路径p的长度 +int belong[N]; //belong[v]表示节点v所属的路径编号 +int idx[N]; //idx[v]表示节点v在其路径中的编号, 按深度由深到浅依次标号 +int dep[N]; //dep[v]表示节点v的深度 +int fa[N]; //fa[v]表示节点v的父亲节点 +int size[N]; //size[v]表示以节点v为根的子树的节点个数 +int que[N]; +bool vis[N]; +int n, cnt; //n是点数, 标号从1到n +void split() { + memset(dep, 0xff, sizeof(dep)); + int l = 0, r = 0; + que[++r] = 1; dep[1] = 0; fa[1] = -1; + while (l < r) { + int u = que[++l]; + vis[u] = false; + for (int i = g.head[u]; ~i; i = g.next[i]) { + int v = g.to[i]; + if (!~dep[v]) { que[++r] = v; dep[v] = dep[u] + 1; fa[v] = u; } + } + } + cnt = 0; + for (int i = n; i > 0; --i) { + int u = que[i], p = -1; + size[u] = 1; + for (int j = g.head[u]; ~j; j = g.next[j]) { + int v = g.to[j]; + if (vis[v]) { + size[u] += size[v]; + if (!~p || size[v] > size[p]) { p = v; } + } + } + if (!~p) { + idx[u] = len[++cnt] = 1; + belong[u] = cnt; + top[cnt] = u; + } else { + belong[u] = belong[p]; + idx[u] = ++len[belong[u]]; + top[belong[u]] = u; + } + vis[u] = true; + } +} +int fi[N], cid[N], rank[N]; +void getcid() { + fi[1] = 1; + for (int i = 2; i <= cnt; ++i) { fi[i] = fi[i - 1] + len[i - 1]; } + for (int i = 1; i <= n; ++i) { + cid[i] = fi[belong[i]] + len[belong[i]] - idx[i]; + rank[cid[i]] = i; + } +} +// 路径修改和查询依下面修改 +int query(int x, int y) { + int ret = 0; + while (belong[x] != belong[y]) { + if (dep[top[belong[x]]] < dep[top[belong[y]]]) { swap(x, y); } + ret = max(ret, query(cid[top[belong[x]]], cid[x], 1, n, 1)); + x = fa[top[belong[x]]]; + } + if (dep[x] > dep[y]) { swap(x, y); } + ret = max(ret, query(cid[x], cid[y], 1, n, 1)); + /*边权如下 + if(x!=y) + ret=max(ret,query(cid[x]+1,cid[y],1,n,1)); + */ + return ret; +} +//第一次dfs和倍增LCA的dfs部分几乎一致; 所以稍作修改就可以无缝衔接LCA. +//第二次dfs对节点的新位置进行了标号(对应bfs的getcid函数), +//我们可以发现无论它以怎样的顺序进行dfs(先dfs重儿子再dfs其它子节点), 得到的依旧是这棵树的一个dfs序. +//换句话说, 这里处理出的剖分标号, 同时也是dfs序标号. +//我们知道每棵子树的节点在dfs序中都是连续的一段, +//这样我们就可以同时维护树上路径信息(剖分部分复杂度O(logn))和子树信息(剖分部分复杂度O(1))了. +//BZOJ3083 树链剖分套线段树 +const int N = 100005; +const int maxd = 18; +const int INF = 0x7fffffff; +struct graph { + int head[N], tot; + int to[N << 1], next[N << 1]; + void init() { + tot = 0; memset(head, 0xff, sizeof(head)); + } + void add(int u, int v) { + to[tot] = v; next[tot] = head[u]; head[u] = tot++; + } +} g; +int top[N], son[N]; +int dep[N], fa[N][maxd], size[N]; +int cid[N], rank[N], cnt; +void dfs1(int u) { + size[u] = 1; son[u] = -1; + for (int i = 1; i < maxd; ++i) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; } + for (int i = g.head[u]; ~i; i = g.next[i]) { + int v = g.to[i]; + if (v != fa[u][0]) { + dep[v] = dep[u] + 1; fa[v][0] = u; + dfs1(v); + size[u] += size[v]; + if (!~son[u] || size[v] > size[son[u]]) { son[u] = v; } + } + } +} +void dfs2(int u, int tp) { + top[u] = tp; cid[u] = ++cnt; rank[cid[u]] = u; + if (~son[u]) { dfs2(son[u], tp); } + for (int i = g.head[u]; ~i; i = g.next[i]) { + int v = g.to[i]; + if (v != son[u] && v != fa[u][0]) { dfs2(v, v); } + } +} +void split() { + dfs1(1); cnt = 0; dfs2(1, 1); +} +int lca(int u, int v) { + if (dep[u] < dep[v]) { swap(u, v); } + int k = dep[u] - dep[v]; + for (int i = 0; i < maxd; ++i) { + if ((1 << i)&k) { u = fa[u][i]; } + } + if (u == v) { return u; } + for (int i = maxd - 1; i >= 0; --i) { + if (fa[u][i] != fa[v][i]) { u = fa[u][i]; v = fa[v][i]; } + } + return fa[u][0]; +} +int n, root, a[N]; +#define lson l,m,rt<<1 +#define rson m+1,r,rt<<1|1 +int vmin[N << 2], col[N << 2]; +void push_up(int rt) { + vmin[rt] = min(vmin[rt << 1], vmin[rt << 1 | 1]); +} +void push_down(int rt) { + if (col[rt]) { + col[rt << 1] = col[rt << 1 | 1] = vmin[rt << 1] = vmin[rt << 1 | 1] = col[rt]; + col[rt] = 0; + } +} +void build(int l, int r, int rt) { + col[rt] = 0; + if (l == r) { vmin[rt] = a[rank[l]]; return; } + int m = l + r >> 1; + build(lson); + build(rson); + push_up(rt); +} +void update(int L, int R, int val, int l, int r, int rt) { + if (L <= l && r <= R) { col[rt] = vmin[rt] = val; return; } + push_down(rt); + int m = l + r >> 1; + if (L <= m) { update(L, R, val, lson); } + if (m < R) { update(L, R, val, rson); } + push_up(rt); +} +int query(int L, int R, int l, int r, int rt) { + if (L <= l && r <= R) { return vmin[rt]; } + push_down(rt); + int m = l + r >> 1; + int ret = INF; + if (L <= m) { ret = min(ret, query(L, R, lson)); } + if (m < R) { ret = min(ret, query(L, R, rson)); } + return ret; +} +void modify(int x, int y, int d) { + while (top[x] != top[y]) { + if (dep[top[x]] < dep[top[y]]) { swap(x, y); } + update(cid[top[x]], cid[x], d, 1, n, 1); + x = fa[top[x]][0]; + } + if (dep[x] > dep[y]) { swap(x, y); } + update(cid[x], cid[y], d, 1, n, 1); +} +int query(int rt) { + if (rt == root) { return query(1, n, 1, n, 1); } + int pre = lca(root, rt); + if (pre != rt) { return query(cid[rt], cid[rt] + size[rt] - 1, 1, n, 1); } + int depth = dep[root] - dep[rt] - 1, tmp = root; + for (int i = maxd - 1; i >= 0; --i) { + if (depth & (1 << i)) { tmp = fa[tmp][i]; } + } + return min(query(1, cid[tmp] - 1, 1, n, 1), query(cid[tmp] + size[tmp], n, 1, n, 1)); +} +int main() { + int m, u, v, opt, id; + while (~scanf("%d%d", &n, &m)) { + g.init(); + for (int i = 1; i < n; ++i) { + scanf("%d%d", &u, &v); + g.add(u, v); g.add(v, u); + } + for (int i = 1; i <= n; ++i) { + scanf("%d", &a[i]); + } + split(); + build(1, n, 1); + scanf("%d", &root); + while (m--) { + scanf("%d", &opt); + switch (opt) { + case 1: scanf("%d", &root); break; + case 2: scanf("%d%d%d", &u, &v, &id); modify(u, v, id); break; + case 3: scanf("%d", &id); printf("%d\n", query(id)); break; + } + } + } +} +//KD-Tree +//用来维护多维第K近点对距离一类的信息. +//在每一维上依次用一个超平面进行空间划分, 将点集比较均匀地分割在各个区域内, +//结构上则是一棵二叉树, 且与线段树的形态和构造方法都有些类似. +//经过改造的KD-Tree一般可以做到O(logn)的单点插入, 以及O(n^(1-1/D))的询问操作, 其中D是维数, 可见维数越大KD-Tree越慢 +//询问距离一个点的前K近点一般需要用一个优先队列进行询问时的维护 +//HDU4347 O(n) 不支持点的插入和删除 +const int N = 50005; +const int INF = ~0U >> 1; +const int DIM = 5; +#define lson l,m-1,dep+1 +#define rson m+1,r,dep+1 +int cur, K; +struct point { + int x[DIM]; + bool operator<(const point &oth)const { return x[cur] < oth.x[cur]; } + void output() { + for (int i = 0; i < K; ++i) { + printf("%d%c", x[i], i < K - 1 ? ' ' : '\n'); + } + } +} vec[N], origin[N], pt, ans[10]; +inline int sqr(int x) { return x * x; } +int dist(const point &a, const point &b) { + int ret = 0; + for (int i = 0; i < K; ++i) { ret += sqr(a.x[i] - b.x[i]); } + return ret; +} +void build(int l, int r, int dep = 0) { + if (l >= r) { return; } + int m = l + r >> 1; + cur = dep % K; + nth_element(vec + l, vec + m, vec + r + 1); + build(lson); + build(rson); +} +priority_queue> pq; +void query(const point &x, int k, int l, int r, int dep = 0) { + if (l > r) { return; } + int m = l + r >> 1, cur = dep % K; + pair tmp(dist(x, vec[m]), vec[m]); + if (pq.size() < k) { + pq.push(tmp); + } else if (pq.top().first > tmp.first) { + pq.pop(); pq.push(tmp); + } + if (x.x[cur] < vec[m].x[cur]) { + query(x, k, lson); + if (pq.top().first > sqr(x.x[cur] - vec[m].x[cur])) { query(x, k, rson); } + } else { + query(x, k, rson); + if (pq.top().first > sqr(x.x[cur] - vec[m].x[cur])) { query(x, k, lson); } + } +} +int main() { + int n, t, m; + while (~scanf("%d%d", &n, &K)) { + for (int i = 1; i <= n; ++i) { + for (int j = 0; j < K; ++j) { + scanf("%d", &origin[i].x[j]); + } + vec[i] = origin[i]; + } + build(1, n); + scanf("%d", &t); + while (t--) { + for (int i = 0; i < K; ++i) { + scanf("%d", &pt.x[i]); + } + scanf("%d", &m); + query(pt, m, 1, n); + for (int i = 0; i < m; ++i) { + ans[i] = pq.top().second; pq.pop(); + } + printf("the closest %d points are:\n", m); + for (int i = m - 1; i >= 0; --i) { ans[i].output(); } + } + } +} +//支持点的插入和删除 +#define lson kdt[rt].ls,dep+1 +#define rson kdt[rt].rs,dep+1 +struct kdnode { + int ls, rs, x[DIM]; bool flag; //删点标记 +} kdt[N]; +inline ll sqr(int x) { return (ll)x * x; } +ll dist(const kdnode &a, const kdnode &b) { + ll ret = 0; + for (int i = 0; i < DIM; ++i) { ret += sqr(a.x[i] - b.x[i]); } + return ret; +} +int root, tot; +void init() { tot = 0; root = -1; } +int add(int pt[]) { + kdt[tot].flag = false; + kdt[tot].ls = kdt[tot].rs = -1; + for (int i = 0; i < DIM; ++i) { kdt[tot].x[i] = pt[i]; } + return tot++; +} +void insert(int pt[], int rt, int dep = 0) { + dep %= DIM; + if (pt[dep] < kdt[rt].x[dep]) { + if (!~kdt[rt].ls) { kdt[rt].ls = add(pt); } + else { insert(pt, lson); } + } else { + if (!~kdt[rt].rs) { kdt[rt].rs = add(pt); } + else { insert(pt, rson); } + } +} +//求最近点距离 +ll query(const kdnode &pt, int rt, int dep = 0) { + if (!~rt) { return INF; } + dep %= DIM; + ll ret = INF, tmp = sqr(kdt[rt].x[dep] - pt.x[dep]); + if (!kdt[rt].flag) { ret = dist(kdt[rt], pt); } + if (pt.x[dep] <= kdt[rt].x[dep]) { + ret = min(ret, query(pt, lson)); + if (tmp < ret) { ret = min(ret, query(pt, rson)); } + } + if (pt.x[dep] >= kdt[rt].x[dep]) { + ret = min(ret, query(pt, rson)); + if (tmp < ret) { ret = min(ret, query(pt, lson)); } + } + return ret; +} +//查询区间内有多少个点 +int query(int pt1[], int pt2[], int rt, int dep = 0) { + if (!~rt) { return 0; } + dep %= DIM; + int ret = 0, cur; + for (cur = 0; cur < DIM; ++cur) { + if (kdt[rt].x[cur] < pt1[cur] || kdt[rt].x[cur] > pt2[cur]) { break; } + } + if (cur == DIM) { ++ret; } + if (pt2[dep] < kdt[rt].x[dep]) { + ret += query(pt1, pt2, lson); + } else if (pt1[dep] >= kdt[rt].x[dep]) { + ret += query(pt1, pt2, rson); + } else { + ret += query(pt1, pt2, lson); + ret += query(pt1, pt2, rson); + } + return ret; +} +//划分树 +int part[20][N]; //表示每层每个位置的值 +int sorted[N]; //已经排序好的数 +int tol[20][N]; //tol[p][i] 表示第i层从1到i有数分入左边 +void build(int l, int r, int dep) { + if (l == r) { return; } + int m = l + r >> 1, cnt = m - l + 1; //表示等于中间值而且被分入左边的个数 + for (int i = l; i <= r; ++i) { + if (part[dep][i] < sorted[m]) { --cnt; } + } + int lpos = l, rpos = m + 1; + for (int i = l; i <= r; ++i) { + if (part[dep][i] < sorted[m]) { + part[dep + 1][lpos++] = part[dep][i]; + } else if (part[dep][i] == sorted[m] && cnt > 0) { + part[dep + 1][lpos++] = part[dep][i]; + --cnt; + } else { + part[dep + 1][rpos++] = part[dep][i]; + } + tol[dep][i] = tol[dep][l - 1] + lpos - l; + } + build(l, m, dep + 1); + build(m + 1, r, dep + 1); +} +//离线查询区间第k大的数, [L, R]是要查询的小区间, [l, r]是大区间 +int query(int L, int R, int k, int l, int r, int dep) { + if (L == R) { return part[dep][L]; } + int m = l + r >> 1, cnt = tol[dep][R] - tol[dep][L - 1]; + if (cnt >= k) { + int tl = l + tol[dep][L - 1] - tol[dep][l - 1], tr = tl + cnt - 1; + return query(tl, tr, k, l, m, dep + 1); + } else { + int tr = R + tol[dep][r] - tol[dep][R], tl = tr - (R - L - cnt); + return query(tl, tr, k - cnt, m + 1, r, dep + 1); + } +} +//左偏树 +//可并堆的一种实现, 可以在O(logn)的时间内实现堆的push、pop和两个堆的合并操作, 以及O(1)时间的取堆顶操作 +int val[N], ls[N], rs[N], dep[N], fa[N]; +void init(int n) { + for (int i = 1; i <= n; ++i) { + scanf("%d", &val[i]); ls[i] = rs[i] = dep[i] = 0; fa[i] = i; + } +} +int find(int x) { return x == fa[x] ? x : fa[x] = findfa(fa[x]); } +int merge(int x, int y) { + if (!x || !y) { return x | y; } + if (val[x] < val[y]) { swap(x, y); } + rs[x] = merge(rs[x], y); fa[rs[x]] = x; + if (dep[ls[x]] < dep[rs[x]]) { swap(ls[x], rs[x]); } + dep[x] = dep[rs[x]] + 1; + return x; +} +int push(int x, int y) { return merge(x, y); } +int pop(int x) { + int a = ls[x], b = rs[x]; + ls[x] = rs[x] = dep[x] = 0; + fa[x] = x; fa[a] = a; fa[b] = b; + return merge(a, b); +} +//POJ 2201 +int main() { + int n, m, x, y; + while (~scanf("%d", &n)) { + init(n); + scanf("%d", &m); + while (m--) { + scanf("%d%d", &x, &y); + int a = find(x), b = find(y); + if (a == b) { + puts("-1"); + } else { + val[a] >>= 1; val[b] >>= 1; + a = push(pop(a), a); b = push(pop(b), b); + printf("%d\n", val[merge(a, b)]); + } + } + } +} +//笛卡尔树 +//考虑一个键值对的序列, 当键与键, 值与值之间互不相同时, 它们可以唯一地构成这样一棵二叉树: +//key在中序遍历时呈升序, 满足二叉查找树性质; 父节点的value大于子节点的value, 满足堆的性质. +//一个键值对序列的笛卡儿树可以O(n)时间内构造出来 +//POJ2201 +const int N = 50005; +int idx[N], n; +struct Cartesian_Tree { + int root, key[N], val[N], ch[N][2], pre[N]; + void init() { + for (int i = 1; i <= n; ++i) { + scanf("%d%d", &key[i], &val[i]); + ch[i][0] = ch[i][1] = pre[i] = 0; + } + } + void build() { + static int st[N]; + int top = -1; + for (int i = 1; i <= n; ++i) { + int k = top; + while (k >= 0 && val[st[k]] > val[idx[i]]) { + --k; + } + if (~k) { + pre[idx[i]] = st[k]; + ch[st[k]][1] = idx[i]; + } + if (k < top) { + pre[st[k + 1]] = idx[i]; + ch[idx[i]][0] = st[k + 1]; + } + st[++k] = idx[i]; + top = k; + } + root = st[0]; + } +} ct; +bool cmp(int x, int y) { + return ct.key[x] < ct.key[y]; +} +int main() { + while (~scanf("%d", &n)) { + ct.init(); + for (int i = 1; i <= n; ++i) { idx[i] = i; } + sort(idx + 1, idx + n + 1, cmp); + ct.build(); + puts("YES"); + for (int i = 1; i <= n; ++i) { + printf("%d %d %d\n", ct.pre[i], ct.ch[i][0], ct.ch[i][1]); + } + } +} diff --git a/.ACM-Templates/日期.cpp b/.ACM-Templates/日期.cpp new file mode 100644 index 0000000..787c770 --- /dev/null +++ b/.ACM-Templates/日期.cpp @@ -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; +} diff --git a/.ACM-Templates/矩阵.cpp b/.ACM-Templates/矩阵.cpp new file mode 100644 index 0000000..57695ee --- /dev/null +++ b/.ACM-Templates/矩阵.cpp @@ -0,0 +1,131 @@ +//矩阵类 +template struct mat { + vector 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; + } +}; diff --git a/.ACM-Templates/稀疏表.cpp b/.ACM-Templates/稀疏表.cpp new file mode 100644 index 0000000..f2dfe61 --- /dev/null +++ b/.ACM-Templates/稀疏表.cpp @@ -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])); +} diff --git a/.ACM-Templates/组合数学.cpp b/.ACM-Templates/组合数学.cpp new file mode 100644 index 0000000..00bbdb6 --- /dev/null +++ b/.ACM-Templates/组合数学.cpp @@ -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] diff --git a/.ACM-Templates/莫队.cpp b/.ACM-Templates/莫队.cpp new file mode 100644 index 0000000..ea3f72ef --- /dev/null +++ b/.ACM-Templates/莫队.cpp @@ -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]); } + } +} diff --git a/.ACM-Templates/计算几何.cpp b/.ACM-Templates/计算几何.cpp new file mode 100644 index 0000000..963489f --- /dev/null +++ b/.ACM-Templates/计算几何.cpp @@ -0,0 +1,1393 @@ +//随机化求最小圆覆盖 +//圆心为O, 半径为R +double R, EPS = 1e-9; +struct Point { double x, y; } a[N], O; +inline double dis(Point x, Point y) { return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y)); } +Point center(Point x, Point y, Point z) { + double a1 = y.x - x.x, a2 = z.x - x.x, b1 = y.y - x.y, b2 = z.y - x.y, + c1 = (a1 * a1 + b1 * b1) / 2, c2 = (a2 * a2 + b2 * b2) / 2, + d = a1 * b2 - a2 * b1; + return (Point) {x.x + (c1 * b2 - c2 * b1) / d, x.y + (a1 * c2 - a2 * c1) / d}; +} +void cal(int n, Point b[]) { + O = a[0]; R = 0; + for (int i = 0; i < n; i++) { a[i] = b[i]; } + for (int i = 0; i < n; i++) { swap(a[rand() % n], a[i]); } + for (int i = 1; i < n; i++) { + if (dis(a[i], O) > R + EPS) { + O = a[i]; R = 0; + for (int j = 0; j < i; j++) { + if (dis(a[j], O) > R + EPS) { + O = (Point) {(a[i].x + a[j].x) / 2, (a[i].y + a[j].y) / 2}, R = dis(O, a[i]); + for (int k = 0; k < j; k++) { + if (dis(a[k], O) > R + EPS) { O = center(a[k], a[j], a[i]), R = dis(O, a[i]); } + } + } + } + } + } +} + +//kuangbin +//1、基本函数 +//1.1 Point 定义 +const double PI = acos(-1.0); +const double EPS = 1e-8; +inline int sgn(double x) { + return (fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1)); +} +struct Point { + double x, y; + Point() {} + Point(double _x, double _y): x(_x), y(_y) {} + Point operator-(const Point &b)const { + return Point(x - b.x, y - b.y); + } + //叉积 + double operator^(const Point &b)const { + return x * b.y - y * b.x; + } + //点积 + double operator*(const Point &b)const { + return x * b.x + y * b.y; + } + //绕原点旋转角度B(弧度值), 后x, y的变化 + void transXY(double B) { + double tx = x, ty = y; + x = tx * cos(B) - ty * sin(B); + y = tx * sin(B) + ty * cos(B); + } +}; +//1.2 Line 定义 +struct Line { + Point s, e; + Line() {} + Line(Point _s, Point _e): s(_s), e(_e) {} + //两直线相交求交点 + //第一个值为0表示直线重合, 为1表示平行, 为0表示相交, 为2是相交 + //只有第一个值为2时, 交点才有意义 + pair operator&(const Line &b)const { + Point res = s; + if (sgn((s - e) ^ (b.s - b.e)) == 0) { + if (sgn((s - b.e) ^ (b.s - b.e)) == 0) { return make_pair(0, res); } //重合 + else { return make_pair(1, res); } //平行 + } + double t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e)); + res.x += (e.x - s.x) * t; + res.y += (e.y - s.y) * t; + return make_pair(2, res); + } +}; +//1.3 两点间距离 +//*两点间距离 +double dist(const Point &a, const Point &b) { + return sqrt((a - b) * (a - b)); +} +//1.4 判断: 线段相交 +//*判断线段相交 +bool inter(const Line &l1, const Line &l2) { + return max(l1.s.x, l1.e.x) >= min(l2.s.x, l2.e.x) + && max(l2.s.x, l2.e.x) >= min(l1.s.x, l1.e.x) + && max(l1.s.y, l1.e.y) >= min(l2.s.y, l2.e.y) + && max(l2.s.y, l2.e.y) >= min(l1.s.y, l1.e.y) + && sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e-l1.e) ^ (l1.s - l1.e)) <= 0 + && sgn((l1.s - l2.e) ^ (l2.s - l2.e)) * sgn((l1.e-l2.e) ^ (l2.s - l2.e)) <= 0; +} +//1.5 判断:直线和线段相交 +//判断直线和线段相交 +bool Seg_inter_line(const Line &l1, const Line &l2) { //判断直线l1和线段l2是否相交 + return sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e-l1.e) ^ (l1.s - l1.e)) <= 0; +} +//1.6 点到直线距离 +//点到直线距离 +//返回为result, 是点到直线最近的点 +Point PointToLine(const Point &P, const Line &L) { + double t = ((P - L.s) * (L.e-L.s)) / ((L.e - L.s) * (L.e - L.s)); + return Point(L.s.x + (L.e.x - L.s.x) * t, L.s.y + (L.e.y - L.s.y) * t); +} +//1.7 点到线段距离 +//点到线段的距离 +//返回点到线段最近的点 +Point NearestPointToLineSeg(const Point &P, const Line &L) { + double t = ((P - L.s) * (L.e-L.s)) / ((L.e-L.s) * (L.e-L.s)); + Point result; + if (t >= 0 && t <= 1) { + result.x = L.s.x + (L.e.x - L.s.x) * t; + result.y = L.s.y + (L.e.y - L.s.y) * t; + } else { + if (dist(P, L.s) < dist(P, L.e)) { result = L.s; } + else { result = L.e; } + } + return result; +} +//1.8 计算多边形面积 +//计算多边形面积 +//点的编号从0 ~ n - 1 +double CalcArea(Point p[], int n) { + double res = 0; + for (int i = 0; i < n; i++) { + res += (p[i] ^ p[(i + 1) % n]) * 0.5; + } + return fabs(res); +} +//1.9 判断点在线段上 +//*判断点在线段上 +bool OnSeg(const Point &P, const Line &L) { + return sgn((L.s - P) ^ (L.e-P)) == 0 + && sgn((P.x - L.s.x) * (P.x - L.e.x)) <= 0 + && sgn((P.y - L.s.y) * (P.y - L.e.y)) <= 0; +} +//1.10 判断点在凸多边形内 +//*判断点在凸多边形内 +//点形成一个凸包, 而且按逆时针排序(如果是顺时针把里面的<0改为>0) +//点的编号:0 ~ n - 1 +//返回值: +//-1:点在凸多边形外 +//0:点在凸多边形边界上 +//1:点在凸多边形内 +int inConvexPoly(const Point &a, Point p[], int n) { + for (int i = 0; i < n; i++) { + if (sgn((p[i] - a) ^ (p[(i + 1) % n] - a)) < 0) { return -1; } + else if (OnSeg(a, Line(p[i], p[(i + 1) % n]))) { return 0; } + } + return 1; +} +//1.11 判断点在任意多边形内 +//*判断点在任意多边形内 +//射线法, poly[]的顶点数要大于等于3,点的编号0~n-1 +//返回值 +//-1:点在凸多边形外 +//0:点在凸多边形边界上 +//1:点在凸多边形内 +int inPoly(const Point &p, Point poly[], int n) { + int cnt = 0; + Line ray(p, Point(-100000000000.0, p.y)), side; //-INF,注意取值防止越界 + for (int i = 0; i < n; i++) { + side.s = poly[i]; + side.e = poly[(i + 1) % n]; + if (OnSeg(p, side)) { return 0; } + //如果平行轴则不考虑 + if (sgn(side.s.y - side.e.y) == 0) { continue; } + if (OnSeg(side.s, ray)) { + if (sgn(side.s.y - side.e.y) > 0) { cnt++; } + } else if (OnSeg(side.e, ray)) { + if (sgn(side.e.y - side.s.y) > 0) { cnt++; } + } else if (inter(ray, side)) { + cnt++; + } + } + if (cnt & 1) { return 1; } + else { return -1; } +} +//1.12 判断凸多边形 +//判断凸多边形 +//允许共线边 +//点可以是顺时针给出也可以是逆时针给出 +//点的编号1~n-1 +bool isconvex(Point poly[], int n) { + bool s[3] = {0}; + for (int i = 0; i < n; i++) { + s[sgn((poly[(i + 1) % n] - poly[i]) ^ (poly[(i + 2) % n] - poly[i])) + 1] = true; + if (s[0] && s[2]) { return false; } + } + return true; +} +//2、凸包 +/* +* 求凸包, Graham算法 +* 点的编号0 ~ n - 1 +* 返回凸包结果Stack[0 ~ top - 1]为凸包的编号 +*/ +const int N = 1010; +Point lst[N]; +int Stack[N], top; +//相对于lst[0]的极角排序 +bool _cmp(const Point &p1, const Point &p2) { + double tmp = (p1 - lst[0]) ^ (p2 - lst[0]); + if (sgn(tmp) > 0) { return true; } + else if (sgn(tmp) == 0 && sgn(dist(p1, lst[0]) - dist(p2, lst[0])) <= 0) { return true; } + else { return false; } +} +void Graham(int n) { + Point p0 = lst[0]; + int k = 0; + //找最下边的一个点 + for (int i = 1; i < n; i++) { + if ((p0.y > lst[i].y) || (p0.y == lst[i].y && p0.x > lst[i].x)) { + p0 = lst[i]; k = i; + } + } + swap(lst[k], lst[0]); + sort(lst + 1, lst + n, _cmp); + if (n == 1) { + top = 1; + Stack[0] = 0; + return; + } + if (n == 2) { + top = 2; + Stack[0] = 0; + Stack[1] = 1; + return ; + } + top = 2; + Stack[0] = 0; + Stack[1] = 1; + for (int i = 2; i < n; i++) { + while (top > 1 && sgn((lst[Stack[top - 1]] - lst[Stack[top - 2]]) ^ (lst[i] - lst[Stack[top - 2]])) <= 0) { + top--; + } + Stack[top++] = i; + } +} +//3、平面最近点对(HDU 1007) +const double EPS = 1e-6; +const double INF = 1e20; +const int N = 100005; +struct Point { + double x, y; +} p[N], tmpt[N]; +double dist(const Point &a, const Point &b) { + return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); +} +bool cmpxy(const Point &a, const Point &b) { + return a.x < b.x || (a.x == b.x && a.y < b.y); +} +bool cmpy(Point a, Point b) { + return a.y < b.y; +} +double Closest_Pair(int left, int right) { + double d = INF; + if (left == right) { return d; } + if (left + 1 == right) { return dist(p[left], p[right]); } + int mid = (left + right) / 2; + double d1 = Closest_Pair(left, mid); + double d2 = Closest_Pair(mid + 1, right); + d = min(d1, d2); + int k = 0; + for (int i = left; i <= right; i++) { + if (fabs(p[mid].x - p[i].x) <= d) { + tmpt[k++] = p[i]; + } + } + sort(tmpt, tmpt + k, cmpy); + for (int i = 0; i < k; i++) { + for (int j = i + 1; j < k && tmpt[j].y - tmpt[i].y < d; j++) { + d = min(d, dist(tmpt[i], tmpt[j])); + } + } + return d; +} +int main() { + int n; + while (scanf("%d", &n) == 1 && n) { + for (int i = 0; i < n; i++) { + scanf("%lf%lf", &p[i].x, &p[i].y); + } + sort(p, p + n, cmpxy); + printf("%.2lf\n", Closest_Pair(0, n - 1) / 2); + } +} +//4、旋转卡壳 +//4.1 求解平面最远点对(POJ 2187 Beauty Contest) +struct Point { + int x, y; + Point(int _x = 0, int _y = 0): x(_x), y(_y) {} + Point operator-(const Point &b)const { + return Point(x - b.x, y - b.y); + } + int operator^(const Point &b)const { + return x * b.y - y * b.x; + } + int operator*(const Point &b)const { + return x * b.x + y * b.y; + } + void input() { + scanf("%d%d", &x, &y); + } +}; +//距离的平方 +int dist2(const Point &a, const Point &b) { + return (a - b) * (a - b); +} +//******二维凸包, int*********** +const int N = 50005; +Point lst[N], p[N]; +int Stack[N], top; +bool _cmp(const Point &p1, const Point &p2) { + int tmp = (p1 - lst[0]) ^ (p2 - lst[0]); + if (tmp > 0) { return true; } + else if (tmp == 0 && dist2(p1, lst[0]) <= dist2(p2, lst[0])) { return true; } + else { return false; } +} +void Graham(int n) { + Point p0 = lst[0]; + int k = 0; + for (int i = 1; i < n; i++) { + if (p0.y > lst[i].y || (p0.y == lst[i].y && p0.x > lst[i].x)) { + p0 = lst[i]; + k = i; + } + } + swap(lst[k], lst[0]); + sort(lst + 1, lst + n, _cmp); + if (n == 1) { + top = 1; + Stack[0] = 0; + return; + } + if (n == 2) { + top = 2; + Stack[0] = 0; Stack[1] = 1; + return; + } + top = 2; + Stack[0] = 0; + Stack[1] = 1; + for (int i = 2; i < n; i++) { + while (top > 1 && ((lst[Stack[top - 1]] - lst[Stack[top - 2]]) ^ (lst[i] - lst[Stack[top - 2]])) <= 0) { + top--; + } + Stack[top++] = i; + } +} +//旋转卡壳, 求两点间距离平方的最大值 +int rotating_calipers(Point p[], int n) { + int ans = 0, cur = 1; + Point v; + for (int i = 0; i < n; i++) { + v = p[i] - p[(i + 1) % n]; + while ((v ^ (p[(cur + 1) % n] - p[cur])) < 0) { + cur = (cur + 1) % n; + } + ans = max(ans, max(dist2(p[i], p[cur]), dist2(p[(i + 1) % n], p[(cur + 1) % n]))); + } + return ans; +} +int main() { + int n; + while (~scanf("%d", &n)) { + for (int i = 0; i < n; i++) { lst[i].input(); } + Graham(n); + for (int i = 0; i < top; i++) { p[i] = lst[Stack[i]]; } + printf("%d\n", rotating_calipers(p, top)); + } +} +//4.2 求解平面点集最大三角形 +//旋转卡壳计算平面点集最大三角形面积 +Point lst[N], p[N]; +int rotating_calipers(Point p[], int n) { + int ans = 0; + Point v; + for (int i = 0; i < n; i++) { + int j = (i + 1) % n, k = (j + 1) % n; + while (j != i && k != i) { + ans = max(ans, abs((p[j] - p[i]) ^ (p[k] - p[i]))); + while (((p[i] - p[j]) ^ (p[(k + 1) % n] - p[k])) < 0) { + k = (k + 1) % n; + } + j = (j + 1) % n; + } + } + return ans; +} +int main() { + int n; + while (scanf("%d", &n) == 1) { + if (n == -1) { break; } + for (int i = 0; i < n; i++) { lst[i].input(); } + Graham(n); + for (int i = 0; i < top; i++) { p[i] = lst[Stack[i]]; } + printf("%.2f\n", rotating_calipers(p, top) / 2.0); + } +} +//4.3 求解两凸包最小距离(POJ 3608) +const double EPS = 1e-8; +inline int sgn(double x) { + return (fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1)); +} +struct Point { + double x, y; + Point() {} + Point(double _x = 0, double _y = 0): x(_x), y(_y) {} + Point operator-(const Point &b)const { + return Point(x - b.x, y - b.y); + } + //叉积 + double operator^(const Point &b)const { + return x * b.y - y * b.x; + } + //点积 + double operator*(const Point &b)const { + return x * b.x + y * b.y; + } + void input() { + scanf("%lf%lf", &x, &y); + } +}; +struct Line { + Point s, e; + Line() {} + Line(Point _s, Point _e): s(_s), e(_e) {} +}; +//两点间距离 +double dist(const Point &a, const Point &b) { + return sqrt((a - b) * (a - b)); +} +//点到线段的距离, 返回点到线段最近的点 +Point NearestPointToLineSeg(const Point &P, const Line &L) { + double t = ((P - L.s) * (L.e-L.s)) / ((L.e-L.s) * (L.e-L.s)); + Point result; + if (t >= 0 && t <= 1) { + result.x = L.s.x + (L.e.x - L.s.x) * t; + result.y = L.s.y + (L.e.y - L.s.y) * t; + } else { + if (dist(P, L.s) < dist(P, L.e)) { result = L.s; } + else { result = L.e; } + } + return result; +} +/* +* 求凸包, Graham算法 +* 点的编号0~n-1 +* 返回凸包结果Stack[0~top-1]为凸包的编号 +*/ +const int N = 10005; +Point lst[N]; +int Stack[N], top; +//相对于list[0]的极角排序 +bool _cmp(const Point &p1, const Point &p2) { + double tmp = (p1 - lst[0]) ^ (p2 - lst[0]); + if (sgn(tmp) > 0) { return true; } + else if (sgn(tmp) == 0 && sgn(dist(p1, lst[0]) - dist(p2, lst[0])) <= 0) { return true; } + else { return false; } +} +void Graham(int n) { + Point p0 = lst[0]; + int k = 0; + //找最下边的一个点 + for (int i = 1; i < n; i++) { + if ((p0.y > lst[i].y) || (p0.y == lst[i].y && p0.x > lst[i].x)) { + p0 = lst[i]; + k = i; + } + } + swap(lst[k], lst[0]); + sort(lst + 1, lst + n, _cmp); + if (n == 1) { + top = 1; + Stack[0] = 0; + return; + } + if (n == 2) { + top = 2; + Stack[0] = 0; + Stack[1] = 1; + return ; + } + top = 2; + Stack[0] = 0; + Stack[1] = 1; + for (int i = 2; i < n; i++) { + while (top > 1 && sgn((lst[Stack[top - 1]] - lst[Stack[top - 2]]) ^ (lst[i] - lst[Stack[top - 2]])) <= 0) { + top--; + } + Stack[top++] = i; + } +} +//点p0到线段p1p2的距离 +double pointtoseg(const Point &p0, const Point &p1, const Point &p2) { + return dist(p0, NearestPointToLineSeg(p0, Line(p1, p2))); +} +//平行线段p0p1和p2p3的距离 +double dispallseg(const Point &p0, const Point &p1, const Point &p2, const Point &p3) { + double ans1 = min(pointtoseg(p0, p2, p3), pointtoseg(p1, p2, p3)); + double ans2 = min(pointtoseg(p2, p0, p1), pointtoseg(p3, p0, p1)); + return min(ans1, ans2); +} +//得到向量a1a2和b1b2的位置关系 +double Get_angle(const Point &a1, const Point &a2, const Point &b1, const Point &b2) { + return (a2 - a1) ^ (b1 - b2); +} +double rotating_calipers(Point p[], int np, Point q[], int nq) { + int sp = 0, sq = 0; + for (int i = 0; i < np; i++) { + if (sgn(p[i].y - p[sp].y) < 0) { sp = i; } + } + for (int i = 0; i < nq; i++) { + if (sgn(q[i].y - q[sq].y) > 0) { sq = i; } + } + double ans = dist(p[sp], q[sq]), tmp; + for (int i = 0; i < np; i++) { + while (sgn(tmp = Get_angle(p[sp], p[(sp + 1) % np], q[sq], q[(sq + 1) % nq])) < 0) { + sq = (sq + 1) % nq; + } + if (sgn(tmp) == 0) { + ans = min(ans, dispallseg(p[sp], p[(sp + 1) % np], q[sq], q[(sq + 1) % nq])); + } else { ans = min(ans, pointtoseg(q[sq], p[sp], p[(sp + 1) % np])); } + sp = (sp + 1) % np; + } + return ans; +} +double solve(Point p[], int n, Point q[], int m) { + return min(rotating_calipers(p, n, q, m), rotating_calipers(q, m, p, n)); +} +Point p[N], q[N]; +int main() { + int n, m; + while (scanf("%d%d", &n, &m) == 2) { + if (n == 0 && m == 0) { break; } + for (int i = 0; i < n; i++) { lst[i].input(); } + Graham(n); + for (int i = 0; i < top; i++) { p[i] = lst[i]; } + n = top; + for (int i = 0; i < m; i++) { lst[i].input(); } + Graham(m); + for (int i = 0; i < top; i++) { q[i] = lst[i]; } + m = top; + printf("%.4f\n", solve(p, n, q, m)); + } +} +//5、半平面交 +//5.1 半平面交模板(from UESTC) +const double PI = acos(-1.0); +const double EPS = 1e-8; +const int N = 105; +inline int sgn(double x) { + return (fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1)); +} +struct Point { + double x, y; + Point() {} + Point(double _x, double _y): x(_x), y(_y) {} + Point operator-(const Point &b)const { + return Point(x - b.x, y - b.y); + } + double operator^(const Point &b)const { + return x * b.y - y * b.x; + } + double operator*(const Point &b)const { + return x * b.x + y * b.y; + } +}; +struct Line { + Point s, e; + double k; + Line() {} + Line(Point _s, Point _e): s(_s), e(_e), k(atan2(e.y - s.y, e.x - s.x)) {} + Point operator&(const Line &b)const { + Point res = s; + double t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e)); + res.x += (e.x - s.x) * t; + res.y += (e.y - s.y) * t; + return res; + } +}; +//半平面交, 直线的左边代表有效区域 +bool HPIcmp(const Line &a, const Line &b) { + if (fabs(a.k - b.k) > EPS) { return a.k < b.k; } + return ((a.s - b.s) ^ (b.e - b.s)) < 0; +} +Line Q[N]; +void HPI(Line line[], int n, Point res[], int &resn) { + int tot = 1; + sort(line, line + n, HPIcmp); + for (int i = 1; i < n; i++) { + if (fabs(line[i].k - line[i - 1].k) > EPS) { line[tot++] = line[i]; } + } + int head = 0, tail = 1; + Q[0] = line[0]; Q[1] = line[1]; + resn = 0; + for (int i = 2; i < tot; i++) { + if (fabs((Q[tail].e-Q[tail].s) ^ (Q[tail - 1].e-Q[tail - 1].s)) < EPS + || fabs((Q[head].e-Q[head].s) ^ (Q[head + 1].e-Q[head + 1].s)) < EPS) { + return; + } + while (head < tail + && (((Q[tail]&Q[tail - 1]) - line[i].s) ^ (line[i].e-line[i].s)) > EPS) { + tail--; + } + while (head < tail + && (((Q[head]&Q[head + 1]) - line[i].s) ^ (line[i].e-line[i].s)) > EPS) { + head++; + } + Q[++tail] = line[i]; + } + while (head < tail + && (((Q[tail]&Q[tail - 1]) - Q[head].s) ^ (Q[head].e-Q[head].s)) > EPS) { + tail--; + } + while (head < tail + && (((Q[head]&Q[head - 1]) - Q[tail].s) ^ (Q[tail].e-Q[tail].e)) > EPS) { + head++; + } + if (tail <= head + 1) { return; } + for (int i = head; i < tail; i++) { + res[resn++] = Q[i] & Q[i + 1]; + } + if (head < tail - 1) { + res[resn++] = Q[head] & Q[tail]; + } +} +//5.2 普通半平面交写法 +//POJ 1750 +const double EPS = 1e-18; +const double INF = 100000000000.0; +const int N = 105; +inline int sgn(double x) { + return (fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1)); +} +struct Point { + double x, y; + Point() {} + Point(double _x, double _y): x(_x), y(_y) {} + Point operator-(const Point &b)const { + return Point(x - b.x, y - b.y); + } + double operator^(const Point &b)const { + return x * b.y - y * b.x; + } + double operator*(const Point &b)const { + return x * b.x + y * b.y; + } +}; +//计算多边形面积 +double CalcArea(Point p[], int n) { + double res = 0; + for (int i = 0; i < n; i++) { + res += (p[i] ^ p[(i + 1) % n]); + } + return fabs(res / 2); +} +//通过两点, 确定直线方程 +void Get_equation(const Point &p1, const Point &p2, double &a, double &b, double &c) { + a = p2.y - p1.y; + b = p1.x - p2.x; + c = p2.x * p1.y - p1.x * p2.y; +} +//求交点 +Point Intersection(const Point &p1, const Point &p2, double a, double b, double c) { + double u = fabs(a * p1.x + b * p1.y + c); + double v = fabs(a * p2.x + b * p2.y + c); + return Point((p1.x * v + p2.x * u) / (u + v), (p1.y * v + p2.y * u) / (u + v)); +} +Point tp[N]; +void Cut(double a, double b, double c, Point p[], int &cnt) { + int tmp = 0; + for (int i = 1; i <= cnt; i++) { + //当前点在左侧, 逆时针的点 + if (a * p[i].x + b * p[i].y + c < EPS) { tp[++tmp] = p[i]; } + else { + if (a * p[i - 1].x + b * p[i - 1].y + c < -EPS) { + tp[++tmp] = Intersection(p[i - 1], p[i], a, b, c); + } + if (a * p[i + 1].x + b * p[i + 1].y + c < -EPS) { + tp[++tmp] = Intersection(p[i], p[i + 1], a, b, c); + } + } + } + for (int i = 1; i <= tmp; i++) { p[i] = tp[i]; } + p[0] = p[tmp]; + p[tmp + 1] = p[1]; + cnt = tmp; +} +double V[N], U[N], W[N]; +int n; +Point p[N]; +bool solve(int id) { + p[1] = Point(0, 0); + p[2] = Point(INF, 0); + p[3] = Point(INF, INF); + p[4] = Point(0, INF); + p[0] = p[4]; + p[5] = p[1]; + int cnt = 4; + for (int i = 0; i < n; i++) { + if (i != id) { + double a = (V[i] - V[id]) / (V[i] * V[id]); + double b = (U[i] - U[id]) / (U[i] * U[id]); + double c = (W[i] - W[id]) / (W[i] * W[id]); + if (sgn(a) == 0 && sgn(b) == 0) { + if (sgn(c) >= 0) { return false; } + else { continue; } + } + Cut(a, b, c, p, cnt); + } + } + if (sgn(CalcArea(p, cnt)) == 0) { return false; } + else { return true; } +} +int main() { + while (scanf("%d", &n) == 1) { + for (int i = 0; i < n; i++) { + scanf("%lf%lf%lf", &V[i], &U[i], &W[i]); + } + for (int i = 0; i < n; i++) { + if (solve(i)) { printf("Yes\n"); } + else { printf("No\n"); } + } + } +} +//6、三点求圆心坐标(三角形外心) +//过三点求圆心坐标 +Point waixin(const Point &a, const Point &b, const Point &c) { + double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1 * a1 + b1 * b1) / 2; + double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2 * a2 + b2 * b2) / 2; + double d = a1 * b2 - a2 * b1; + return Point(a.x + (c1 * b2 - c2 * b1) / d, a.y + (a1 * c2 - a2 * c1) / d); +} +//7、求两圆相交的面积 +//两个圆的公共部分面积 +double Area_of_overlap(const Point &c1, double r1, const Point &c2, double r2) { + double d = dist(c1, c2); + if (r1 + r2 < d + EPS) { return 0; } + if (d < fabs(r1 - r2) + EPS) { + double r = min(r1, r2); + return PI * r * r; + } + double x = (d * d + r1 * r1 - r2 * r2) / (2 * d); + double t1 = acos(x / r1); + double t2 = acos((d - x) / r2); + return r1 * r1 * t1 + r2 * r2 * t2 - d * r1 * sin(t1); +} +//8、Pick 公式 +//顶点坐标均是整点的简单多边形: 面积 = 内部格点数目 + 边上格点数目 / 2 - 1 + + + +//http://www.cppblog.com/abilitytao/archive/2009/08/04/92171.html + +//常量区 +const double M_PI = acos(-1.0); //PI +const double INF = 1e10; //无穷大 +const double EPS = 1e-8; //计算精度 +const int LEFT = 0; //点在直线左边 +const int RIGHT = 1; //点在直线右边 +const int ONLINE = 2; //点在直线上 +const int CROSS = 0; //两直线相交 +const int COLINE = 1; //两直线共线 +const int PARALLEL = 2; //两直线平行 +const int NOTCOPLANAR = 3; //两直线不共面 +const int INSIDE = 1; //点在图形内部 +const int OUTSIDE = 2; //点在图形外部 +const int BORDER = 3; //点在图形边界 +const int BAOHAN = 1; //大圆包含小圆 +const int NEIQIE = 2; //内切 +const int XIANJIAO = 3; //相交 +const int WAIQIE = 4; //外切 +const int XIANLI = 5; //相离 +/////////////////////////////////////////////////////////////////// +//类型定义区 +struct Point { //二维点或矢量 + double x, y; + double angle, dis; + Point(): x(0), y(0), angle(0), dis(0) {} + Point(double x0, double y0): x(x0), y(y0), angle(0), dis(0) {} +}; +struct Point3D { //三维点或矢量 + double x, y, z; + Point3D(): x(0), y(0), z(0) {} + Point3D(double x0, double y0, double z0): x(x0), y(y0), z(z0) {} +}; +struct Line { //二维的直线或线段 + Point p1, p2; + Line(): p1(), p2() {} + Line(Point p10, Point p20): p1(p10), p2(p20) {} +}; +struct Line3D { //三维的直线或线段 + Point3D p1, p2; + Line3D(): p1(), p2() {} + Line3D(Point3D p10, Point3D p20): p1(p10), p2(p20) {} +}; +struct Rect { //用长宽表示矩形的方法 w, h分别表示宽度和高度 + double w, h; + Rect(): w(0), h(0) {} + Rect(double _w, double _h) : w(_w), h(_h) {} +}; +struct Rect_2 { //表示矩形, 左下角坐标是(xl, yl), 右上角坐标是(xh, yh) + double xl, yl, xh, yh; + Rect_2(): xl(0), yl(0), xh(0), yh(0) {} + Rect_2(double _xl, double _yl, double _xh, double _yh) : xl(_xl), yl(_yl), xh(_xh), yh(_yh) {} +}; +struct Circle { //圆 + Point c; + double r; + Circle(): c(), r(0) {} + Circle(Point _c, double _r) : c(_c), r(_r) {} +}; +typedef vector Polygon; //二维多边形 +typedef vector Points; //二维点集 +typedef vector Points3D; //三维点集 +/////////////////////////////////////////////////////////////////// +//基本函数区 +inline bool ZERO(double x) { //x == 0 + return (fabs(x) < EPS); +} +inline bool ZERO(Point p) { //p == 0 + return (ZERO(p.x) && ZERO(p.y)); +} +inline bool ZERO(Point3D p) { //p == 0 + return (ZERO(p.x) && ZERO(p.y) && ZERO(p.z)); +} +inline bool EQ(double x, double y) { //eqaul, x == y + return (fabs(x - y) < EPS); +} +inline bool NEQ(double x, double y) { //not equal, x != y + return (fabs(x - y) >= EPS); +} +inline bool LT(double x, double y) { //less than, x < y + return (NEQ(x, y) && (x < y)); +} +inline bool GT(double x, double y) { //greater than, x > y + return (NEQ(x, y) && (x > y)); +} +inline bool LEQ(double x, double y) { //less equal, x <= y + return (EQ(x, y) || (x < y)); +} +inline bool GEQ(double x, double y) { //greater equal, x >= y + return (EQ(x, y) || (x > y)); +} +//注意!!! +//如果是一个很小的负的浮点数 +//保留有效位数输出的时候会出现-0.000这样的形式, +//前面多了一个负号 +//这就会导致错误!!!!!! +//因此在输出浮点数之前, 一定要调用次函数进行修正! +inline double FIX(double x) { + return (fabs(x) < EPS) ? 0 : x; +} +///////////////////////////////////////////////////////////////////////////////////// +//二维矢量运算 +bool operator==(Point p1, Point p2) { + return (EQ(p1.x, p2.x) && EQ(p1.y, p2.y)); +} +bool operator!=(Point p1, Point p2) { + return (NEQ(p1.x, p2.x) || NEQ(p1.y, p2.y)); +} +bool operator<(Point p1, Point p2) { + if (NEQ(p1.x, p2.x)) { return (p1.x < p2.x); } + else { return (p1.y < p2.y); } +} +Point operator+(Point p1, Point p2) { + return Point(p1.x + p2.x, p1.y + p2.y); +} +Point operator-(Point p1, Point p2) { + return Point(p1.x - p2.x, p1.y - p2.y); +} +double operator*(Point p1, Point p2) { //计算叉乘 p1 × p2 + return (p1.x * p2.y - p2.x * p1.y); +} +double operator&(Point p1, Point p2) { //计算点积 p1·p2 + return (p1.x * p2.x + p1.y * p2.y); +} +double Norm(Point p) { //计算矢量p的模 + return sqrt(p.x * p.x + p.y * p.y); +} +//把矢量p旋转角度angle (弧度表示) +//angle > 0表示逆时针旋转 +//angle < 0表示顺时针旋转 +Point Rotate(Point p, double angle) { + Point result; + result.x = p.x * cos(angle) - p.y * sin(angle); + result.y = p.x * sin(angle) + p.y * cos(angle); + return result; +} +////////////////////////////////////////////////////////////////////////////////////// +//三维矢量运算 +bool operator==(Point3D p1, Point3D p2) { + return (EQ(p1.x, p2.x) && EQ(p1.y, p2.y) && EQ(p1.z, p2.z)); +} +bool operator<(Point3D p1, Point3D p2) { + if (NEQ(p1.x, p2.x)) { return (p1.x < p2.x); } + else if (NEQ(p1.y, p2.y)) { return (p1.y < p2.y); } + else { return (p1.z < p2.z); } +} +Point3D operator+(Point3D p1, Point3D p2) { + return Point3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); +} +Point3D operator-(Point3D p1, Point3D p2) { + return Point3D(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); +} +Point3D operator*(Point3D p1, Point3D p2) { //计算叉乘 p1 x p2 + return Point3D(p1.y * p2.z - p1.z * p2.y, p1.z * p2.x - p1.x * p2.z, p1.x * p2.y - p1.y * p2.x); +} +double operator&(Point3D p1, Point3D p2) { //计算点积 p1·p2 + return (p1.x * p2.x + p1.y * p2.y + p1.z * p2.z); +} +double Norm(Point3D p) { //计算矢量p的模 + return sqrt(p.x * p.x + p.y * p.y + p.z * p.z); +} +///////////////////////////////////////////////////////////////////////////////////// +//几何题面积计算 +// +//根据三个顶点坐标计算三角形面积 +//面积的正负按照右手旋规则确定 +double Area(Point A, Point B, Point C) { //三角形面积 + return ((B - A) * (C - A) / 2.0); +} +//根据三条边长计算三角形面积 +double Area(double a, double b, double c) { //三角形面积 + double s = (a + b + c) / 2.0; + return sqrt(s * (s - a) * (s - b) * (s - c)); +} +double Area(const Circle &C) { + return M_PI * C.r * C.r; +} +//计算多边形面积 +//面积的正负按照右手旋规则确定 +double Area(const Polygon &poly) { //多边形面积 + double res = 0; + int n = poly.size(); + if (n < 3) { return 0; } + for (int i = 0; i < n; i++) { + res += poly[i].x * poly[(i + 1) % n].y; + res -= poly[i].y * poly[(i + 1) % n].x; + } + return (res / 2.0); +} +///////////////////////////////////////////////////////////////////////////////////// +//点.线段.直线问题 +// +double Distance(Point p1, Point p2) { //2点间的距离 + return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); +} +double Distance(Point3D p1, Point3D p2) { //2点间的距离,三维 + return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z)); +} +double Distance(Point p, Line L) { //求二维平面上点到直线的距离 + return (fabs((p - L.p1) * (L.p2 - L.p1)) / Norm(L.p2 - L.p1)); +} +double Distance(Point3D p, Line3D L) { //求三维空间中点到直线的距离 + return (Norm((p - L.p1) * (L.p2 - L.p1)) / Norm(L.p2 - L.p1)); +} +bool OnLine(Point p, Line L) { //判断二维平面上点p是否在直线L上 + return ZERO((p - L.p1) * (L.p2 - L.p1)); +} +bool OnLine(Point3D p, Line3D L) { //判断三维空间中点p是否在直线L上 + return ZERO((p - L.p1) * (L.p2 - L.p1)); +} +int Relation(Point p, Line L) { //计算点p与直线L的相对关系 ,返回ONLINE,LEFT,RIGHT + double res = (L.p2 - L.p1) * (p - L.p1); + if (EQ(res, 0)) { return ONLINE; } + else if (res > 0) { return LEFT; } + else { return RIGHT; } +} +bool SameSide(Point p1, Point p2, Line L) { //判断点p1, p2是否在直线L的同侧 + double m1 = (p1 - L.p1) * (L.p2 - L.p1); + double m2 = (p2 - L.p1) * (L.p2 - L.p1); + return GT(m1 * m2, 0); +} +bool OnLineSeg(Point p, Line L) { //判断二维平面上点p是否在线段l上 + return (ZERO((L.p1 - p) * (L.p2 - p)) && LEQ((p.x - L.p1.x) * (p.x - L.p2.x), 0) + && LEQ((p.y - L.p1.y) * (p.y - L.p2.y), 0)); +} +bool OnLineSeg(Point3D p, Line3D L) { //判断三维空间中点p是否在线段l上 + return (ZERO((L.p1 - p) * (L.p2 - p)) && EQ(Norm(p - L.p1) + Norm(p - L.p2), Norm(L.p2 - L.p1))); +} +Point SymPoint(Point p, Line L) { //求二维平面上点p关于直线L的对称点 + Point result; + double a = L.p2.x - L.p1.x; + double b = L.p2.y - L.p1.y; + double t = ((p.x - L.p1.x) * a + (p.y - L.p1.y) * b) / (a * a + b * b); + result.x = 2 * L.p1.x + 2 * a * t - p.x; + result.y = 2 * L.p1.y + 2 * b * t - p.y; + return result; +} +bool Coplanar(Points3D points) { //判断一个点集中的点是否全部共面 + Point3D p; + if (points.size() < 4) { return true; } + p = (points[2] - points[0]) * (points[1] - points[0]); + for (size_t i = 3; i < points.size(); i++) { + if (!ZERO(p & (points[i] - points[0]))) { return false; } + } + return true; +} +bool LineIntersect(Line L1, Line L2) { //判断二维的两直线是否相交 + return (!ZERO((L1.p1 - L1.p2) * (L2.p1 - L2.p2))); //是否平行 +} +bool LineIntersect(Line3D L1, Line3D L2) { //判断三维的两直线是否相交 + Point3D p1 = L1.p1 - L1.p2; + Point3D p2 = L2.p1 - L2.p2; + Point3D p = p1 * p2; + if (ZERO(p)) { return false; } //是否平行 + p = (L2.p1 - L1.p2) * (L1.p1 - L1.p2); + return ZERO(p & L2.p2); //是否共面 +} +bool LineSegIntersect(Line L1, Line L2) { //判断二维的两条线段是否相交 + return (GEQ(max(L1.p1.x, L1.p2.x), min(L2.p1.x, L2.p2.x)) && + GEQ(max(L2.p1.x, L2.p2.x), min(L1.p1.x, L1.p2.x)) && + GEQ(max(L1.p1.y, L1.p2.y), min(L2.p1.y, L2.p2.y)) && + GEQ(max(L2.p1.y, L2.p2.y), min(L1.p1.y, L1.p2.y)) && + LEQ(((L2.p1 - L1.p1) * (L1.p2 - L1.p1)) * ((L2.p2 - L1.p1) * (L1.p2 - L1.p1)), 0) && + LEQ(((L1.p1 - L2.p1) * (L2.p2 - L2.p1)) * ((L1.p2 - L2.p1) * (L2.p2 - L2.p1)), 0)); +} +bool LineSegIntersect(Line3D L1, Line3D L2) { //判断三维的两条线段是否相交 + //todo + return true; +} +//计算两条二维直线的交点, 结果在参数P中返回 +//返回值说明了两条直线的位置关系: COLINE -- 共线 PARALLEL -- 平行 CROSS -- 相交 +int CalCrossPoint(Line L1, Line L2, Point &P) { + double A1, B1, C1, A2, B2, C2; + A1 = L1.p2.y - L1.p1.y; + B1 = L1.p1.x - L1.p2.x; + C1 = L1.p2.x * L1.p1.y - L1.p1.x * L1.p2.y; + A2 = L2.p2.y - L2.p1.y; + B2 = L2.p1.x - L2.p2.x; + C2 = L2.p2.x * L2.p1.y - L2.p1.x * L2.p2.y; + if (EQ(A1 * B2, B1 * A2)) { + if (EQ((A1 + B1) * C2, (A2 + B2) * C1)) { return COLINE; } + else { return PARALLEL; } + } else { + P.x = (B2 * C1 - B1 * C2) / (A2 * B1 - A1 * B2); + P.y = (A1 * C2 - A2 * C1) / (A2 * B1 - A1 * B2); + return CROSS; + } +} +//计算两条三维直线的交点, 结果在参数P中返回 +//返回值说明了两条直线的位置关系 COLINE -- 共线 PARALLEL -- 平行 CROSS -- 相交 NONCOPLANAR -- 不公面 +int CalCrossPoint(Line3D L1, Line3D L2, Point3D &P) { + //todo + return 0; +} +//计算点P到直线L的最近点 +Point NearestPointToLine(Point P, Line L) { + Point result; + double a, b, t; + a = L.p2.x - L.p1.x; + b = L.p2.y - L.p1.y; + t = ((P.x - L.p1.x) * a + (P.y - L.p1.y) * b) / (a * a + b * b); + result.x = L.p1.x + a * t; + result.y = L.p1.y + b * t; + return result; +} +//计算点P到线段L的最近点 +Point NearestPointToLineSeg(Point P, Line L) { + Point result; + double a, b, t; + a = L.p2.x - L.p1.x; + b = L.p2.y - L.p1.y; + t = ((P.x - L.p1.x) * a + (P.y - L.p1.y) * b) / (a * a + b * b); + if (GEQ(t, 0) && LEQ(t, 1)) { + result.x = L.p1.x + a * t; + result.y = L.p1.y + b * t; + } else { + if (Norm(P - L.p1) < Norm(P - L.p2)) { result = L.p1; } + else { result = L.p2; } + } + return result; +} +//计算险段L1到线段L2的最短距离 +double MinDistance(Line L1, Line L2) { + double d1, d2, d3, d4; + if (LineSegIntersect(L1, L2)) { + return 0; + } else { + d1 = Norm(NearestPointToLineSeg(L1.p1, L2) - L1.p1); + d2 = Norm(NearestPointToLineSeg(L1.p2, L2) - L1.p2); + d3 = Norm(NearestPointToLineSeg(L2.p1, L1) - L2.p1); + d4 = Norm(NearestPointToLineSeg(L2.p2, L1) - L2.p2); + return min(min(d1, d2), min(d3, d4)); + } +} +//求二维两直线的夹角, +//返回值是0~Pi之间的弧度 +double Inclination(Line L1, Line L2) { + Point u = L1.p2 - L1.p1; + Point v = L2.p2 - L2.p1; + return acos((u & v) / (Norm(u) * Norm(v))); +} +//求三维两直线的夹角, +//返回值是0~Pi之间的弧度 +double Inclination(Line3D L1, Line3D L2) { + Point3D u = L1.p2 - L1.p1; + Point3D v = L2.p2 - L2.p1; + return acos((u & v) / (Norm(u) * Norm(v))); +} +///////////////////////////////////////////////////////////////////////////// +//多边行问题: +//判断点p是否在凸多边形poly内 +//poly的顶点数目要大于等于3 +//返回值为: +//INSIDE -- 点在poly内 +//BORDER -- 点在poly边界上 +//OUTSIDE -- 点在poly外 +int InsideConvex(Point p, const Polygon &poly) { //判断点p是否在凸多边形poly内 + Point q(0, 0); + Line side; + int i, n = poly.size(); + for (i = 0; i < n; i++) { + q.x += poly[i].x; q.y += poly[i].y; + } + q.x /= n; q.y /= n; + for (i = 0; i < n; i++) { + side.p1 = poly[i]; + side.p2 = poly[(i + 1) % n]; + if (OnLineSeg(p, side)) { return BORDER; } + else if (!SameSide(p, q, side)) { return OUTSIDE; } + } + return INSIDE; +} +//判断多边形poly是否是凸的 +bool IsConvex(const Polygon &poly) { + int i, n, rel; + Line side; + n = poly.size(); + if (n < 3) { return false; } + side.p1 = poly[0]; + side.p2 = poly[1]; + rel = Relation(poly[2], side); + for (i = 1; i < n; i++) { + side.p1 = poly[i]; + side.p2 = poly[(i + 1) % n]; + if (Relation(poly[(i + 2) % n], side) != rel) { return false; } + } + return true; +} +//判断点p是否在简单多边形poly内, 多边形可以是凸的或凹的 +//poly的顶点数目要大于等于3 +//返回值为: +//INSIDE -- 点在poly内 +//BORDER -- 点在poly边界上 +//OUTSIDE -- 点在poly外 +int InsidePolygon(const Polygon &poly, Point p) { + int i, n, count; + Line ray, side; + n = poly.size(); + count = 0; + ray.p1 = p; + ray.p2.y = p.y; + ray.p2.x = - INF; + for (i = 0; i < n; i++) { + side.p1 = poly[i]; + side.p2 = poly[(i + 1) % n]; + if (OnLineSeg(p, side)) { + return BORDER; + } + //如果side平行x轴则不作考虑 + if (EQ(side.p1.y, side.p2.y)) { continue; } + if (OnLineSeg(side.p1, ray)) { + if (GT(side.p1.y, side.p2.y)) { count++; } + } else if (OnLineSeg(side.p2, ray)) { + if (GT(side.p2.y, side.p1.y)) { count++; } + } else if (LineSegIntersect(ray, side)) { + count++; + } + } + return ((count % 2 == 1) ? INSIDE : OUTSIDE); +} +//判断线段是否在多边形内 (线段的点可能在多边形上) +//多边形可以是任意简单多边形 +bool InsidePolygon(const Polygon &poly, Line L) { + bool result; + int n; + Points points; + Point p; + Line side; + result = ((InsidePolygon(poly, L.p1) != OUTSIDE) && (InsidePolygon(poly, L.p2) != OUTSIDE)); + if (!result) { return false; } + n = poly.size(); + for (int i = 0; i < n; i++) { + side.p1 = poly[i]; + side.p2 = poly[(i + 1) % n]; + if (OnLineSeg(L.p1, side)) { + points.push_back(L.p1); + } else if (OnLineSeg(L.p2, side)) { + points.push_back(L.p2); + } else if (OnLineSeg(side.p1, L)) { + points.push_back(side.p1); + } else if (OnLineSeg(side.p2, L)) { + points.push_back(side.p2); + } else if (LineSegIntersect(side, L)) { + return false; + } + } + //对交点进行排序 + sort(points.begin(), points.end()); + for (size_t i = 1; i < points.size(); i++) { + if (points[i - 1] != points[i]) { + p.x = (points[i - 1].x + points[i].x) / 2.0; + p.y = (points[i - 1].y + points[i].y) / 2.0; + if (InsidePolygon(poly, p) == OUTSIDE) { + return false; + } + } + } + return true; +} +//寻找凸包 graham 扫描法 +//生成的多边形顶点按逆时针方向排列 +bool GrahamComp(const Point &left, const Point &right) { + if (EQ(left.angle, right.angle)) { + return (left.dis < right.dis); + } else { + return (left.angle < right.angle); + } +} +void GrahamScan(Points &points, Polygon &result) { + int i, k, n; + Point p; + n = points.size(); + result.clear(); + if (n < 3) { return; } + //选取points中y坐标最小的点points[k], + //如果这样的点有多个, 则取最左边的一个 + k = 0; + for (i = 1; i < n; i++) { + if (EQ(points[i].y, points[k].y)) { + if (points[i].x <= points[k].x) { k = i; } + } else if (points[i].y < points[k].y) { + k = i; + } + } + swap(points[0], points[k]); + //现在points中y坐标最小的点在points[0] + //计算每个点相对于points[0]的极角和距离 + for (i = 1; i < n; i++) { + points[i].angle = atan2(points[i].y - points[0].y, points[i].x - points[0].x); + points[i].dis = Norm(points[i] - points[0]); + } + //对顶点按照相对points[0]的极角从小到大进行排序 + //对于极角相同的按照距points[0]的距离从小到大排序 + sort(points.begin() + 1, points.end(), GrahamComp); + //下面计算凸包 + result.push_back(points[0]); + for (i = 1; i < n; i++) { + //如果有极角相同的点, 只取相对于points[0]最远的一个 + if ((i + 1 < n) && EQ(points[i].angle, points[i + 1].angle)) { continue; } + if (result.size() >= 3) { + p = result[result.size() - 2]; + while (GEQ((points[i] - p) * (result.back() - p), 0)) { + result.pop_back(); + p = result[result.size() - 2]; + } + } + result.push_back(points[i]); + } +} +//用有向直线line切割凸多边形, +//result[LEFT]和result[RIGHT]分别保存被切割后line的左边和右边部分 +//result[ONLINE]没有用到, 只是用来作为辅助空间 +//返回值是切割多边形的切口的长度, +//如果返回值是0 则说明未作切割。 +//当未作切割时, 如果多边形在该直线的右侧, 则result[RIGHT]等于该多边形, 否则result[LEFT]等于该多边形 +//注意:被切割的多边形一定要是凸多边形, 顶点按照逆时针排列 +//可利用这个函数来求多边形的核, 初始的核设为一个很大的矩形, 然后依次用多边形的每条边去割 +double CutConvex(const Polygon &poly, const Line &line, Polygon result[3]) { + vector points; + Line side; + Point p; + int i, n, cur, pre; + result[LEFT].clear(); + result[RIGHT].clear(); + result[ONLINE].clear(); + n = poly.size(); + if (n == 0) { return 0; } + pre = cur = Relation(poly[0], line); + for (i = 0; i < n; i++) { + cur = Relation(poly[(i + 1) % n], line); + if (cur == pre) { + result[cur].push_back(poly[(i + 1) % n]); + } else { + side.p1 = poly[i]; + side.p2 = poly[(i + 1) % n]; + CalCrossPoint(side, line, p); + points.push_back(p); + result[pre].push_back(p); + result[cur].push_back(p); + result[cur].push_back(poly[(i + 1) % n]); + pre = cur; + } + } + sort(points.begin(), points.end()); + if (points.size() < 2) { return 0; } + else { return Norm(points.front() - points.back()); } +} +//求多边形的重心, 适用于凸的或凹的简单多边形 +//该算法可以一边读入多边性的顶点一边计算重心 +Point CenterOfPolygon(const Polygon &poly) { + Point p, p0, p1, p2, p3; + double m, m0; + p1 = poly[0]; + p2 = poly[1]; + p.x = p.y = m = 0; + for (size_t i = 2; i < poly.size(); i++) { + p3 = poly[i]; + p0.x = (p1.x + p2.x + p3.x) / 3.0; + p0.y = (p1.y + p2.y + p3.y) / 3.0; + m0 = p1.x * p2.y + p2.x * p3.y + p3.x * p1.y - p1.y * p2.x - p2.y * p3.x - p3.y * p1.x; + if (ZERO(m + m0)) { m0 += EPS; } //为了防止除0溢出, 对m0做一点点修正 + p.x = (m * p.x + m0 * p0.x) / (m + m0); + p.y = (m * p.y + m0 * p0.y) / (m + m0); + m = m + m0; + p2 = p3; + } + return p; +} +//判断两个矩形是否相交 +//如果相邻不算相交 +bool Intersect(Rect_2 r1, Rect_2 r2) { + return (max(r1.xl, r2.xl) < min(r1.xh, r2.xh) && max(r1.yl, r2.yl) < min(r1.yh, r2.yh)); +} +//判断矩形r2是否可以放置在矩形r1内 +//r2可以任意地旋转 +//发现原来的给出的方法过不了OJ上的无归之室这题, +//所以用了自己的代码 +bool IsContain(Rect r1, Rect r2) { //矩形的w > h + if (r1.w > r2.w && r1.h > r2.h) { return true; } + else { + double r = sqrt(r2.w * r2.w + r2.h * r2.h) / 2.0; + double alpha = atan2(r2.h, r2.w); + double sita = asin((r1.h / 2.0) / r); + double x = r * cos(sita - 2 * alpha); + double y = r * sin(sita - 2 * alpha); + if (x < r1.w / 2.0 && y < r1.h / 2.0 && x > 0 && y > -r1.h / 2.0) { return true; } + else { return false; } + } +} +//////////////////////////////////////////////////////////////////////// +//圆 +Point Center(const Circle &C) { //圆心 + return C.c; +} +//两个圆的公共面积 +double CommonArea(const Circle &A, const Circle &B) { + double area = 0.0; + const Circle &M = (A.r > B.r) ? A : B; + const Circle &N = (A.r > B.r) ? B : A; + double D = Distance(Center(M), Center(N)); + if ((D < M.r + N.r) && (D > M.r - N.r)) { + double cosM = (M.r * M.r + D * D - N.r * N.r) / (2.0 * M.r * D); + double cosN = (N.r * N.r + D * D - M.r * M.r) / (2.0 * N.r * D); + double alpha = 2.0 * acos(cosM); + double beta = 2.0 * acos(cosN); + double TM = 0.5 * M.r * M.r * sin(alpha); + double TN = 0.5 * N.r * N.r * sin(beta); + double FM = (alpha / 360.0) * Area(M); + double FN = (beta / 360.0) * Area(N); + area = FM + FN - TM - TN; + } else if (D <= M.r - N.r) { + area = Area(N); + } + return area; +} +//判断圆是否在矩形内(不允许相切) +bool IsInCircle(const Circle &C, const Rect_2 &rect) { + return (GT(C.c.x - C.r, rect.xl) && LT(C.c.x + C.r, rect.xh) && GT(C.c.y - C.r, rect.yl) + && LT(C.c.y + C.r, rect.yh)); +} +//判断2圆的位置关系 +//返回: +//BAOHAN = 1; //大圆包含小圆 +//NEIQIE = 2; //内切 +//XIANJIAO = 3; //相交 +//WAIQIE = 4; //外切 +//XIANLI = 5; //相离 +int CirCir(const Circle &c1, const Circle &c2) { + double dis = Distance(c1.c, c2.c); + if (LT(dis, fabs(c1.r - c2.r))) { return BAOHAN; } + if (EQ(dis, fabs(c1.r - c2.r))) { return NEIQIE; } + if (LT(dis, c1.r + c2.r) && GT(dis, fabs(c1.r - c2.r))) { return XIANJIAO; } + if (EQ(dis, c1.r + c2.r)) { return WAIQIE; } + return XIANLI; +} diff --git a/.ACM-Templates/输入输出.cpp b/.ACM-Templates/输入输出.cpp new file mode 100644 index 0000000..f63a0fa --- /dev/null +++ b/.ACM-Templates/输入输出.cpp @@ -0,0 +1,71 @@ +//关闭同步 +ios_base::sync_with_stdio(false); cin.tie(NULL); +//输入一个非负整数 +template void read(T &x) { + char c; + while (!isdigit(c = getchar())); + for (x = 0; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; } +} +//输入一个整数 +template 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 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 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 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; +} diff --git a/.ACM-Templates/高精度.cpp b/.ACM-Templates/高精度.cpp new file mode 100644 index 0000000..3116caf --- /dev/null +++ b/.ACM-Templates/高精度.cpp @@ -0,0 +1,195 @@ +//高精度整数 +const int BASE = 1000000000; +const int BASEDIGITS = 9; +struct bint { + vector 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 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 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; + } +}; diff --git a/.ACM-Templates/黑科技.cpp b/.ACM-Templates/黑科技.cpp new file mode 100644 index 0000000..0d63237 --- /dev/null +++ b/.ACM-Templates/黑科技.cpp @@ -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") + +//λ +//ұ10 | (100101111->100100000) | x and (x+1) +//һ01 | (100101111->100111111) | x or (x+1) +//ұ01 | (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 +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 +//ģ +//template, +// typename Tag = pairing_heap_tag, +// typename Allocator = std::allocator> +//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) +//ӻijһԪصֵvoid modify(point_iterator, const_reference) +//ϲvoid join(priority_queue &other)otherϲ*thisother +//ܷ +//ֲpushpopmodifyerasejoin +//pairing_heap_tagpushjoinO(1)̯O(logn) +//binary_heap_tagֻ֧pushpopΪ̯O(logn) +//binomial_heap_tagpushΪ̯O(1)ΪO(logn) +//rc_binomial_heap_tagpushΪO(1)ΪO(logn) +//thin_heap_tagpushΪO(1)֧joinΪO(logn)ֻincrease_keymodify̯O(1) +//ֲ֧Dzã +//½ۣ +//ŻDijkstra㷨pairing_heap_tagϸthin_heap_tagٶдݽṹ൱ +//߶󷨺 +//binary_heap_tagھstd::priority_queue +//pairing_heap_tagھbinomial_heap_tagrc_binomial_heap_tag +//ֻpushpopjoinʱbinary_heap_tagٶȽϿ +//modifyʱԿpairing_heap_tagthin_heap_tagдݽṹ + +//Tree +// +//ext/pb_ds/assoc_container.hppext/pb_ds/tree_policy.hpp +//__gnu_pbds::tree +//ģ +//template, +// typename Tag = rb_tree_tag, +// template +// class Node_Update = null_tree_node_update, +// typename Allocator = std::allocator> +//class tree; +//Tagtreeͣrb_tree_tagsplay_tree_tagov_tree_tag +//Node_UpdateΪգҲpb_dsԴtree_order_statistics_node_updatetreeͻfind_by_orderorder_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)operator[](const Key) +//ijsetֻҪڶMappedΪnull_type4.4.0°汾ıӦnull_mapped_typeͿˡʱָͻpairKeysetû +//ȻһЩ÷磺 +//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 +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(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, 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_childget_r_childȡҶӣǺţһǺֻǻȡiteratorȡڵϢget_metadataȡڵϢ +//operator()ĹǽڵitϢ£end_itʾսڵ +//ܷ +//дݽṹ࣬rb_tree_tagҪ + +//Hash +// +//ext/pb_ds/assoc_container.hppext/pb_ds/hash_policy.hpp +// +//__gnu_pbds::cc_hash_table +//__gnu_pbds::gp_hash_table +//ʹ +//֧findoperator[] + +//Trie +// +//ext/pb_ds/assoc_container.hppext/pb_ds/trie_policy.hpp +//__gnu_pbds::trie +//ģ +//template, +// typename Tag = pat_trie_tag, +// template +// class Node_Update = null_trie_node_update, +// typename Allocator = std::allocator> +//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 prefix_range(key_const_reference) +//Finds the const iterator range corresponding to all values whose prefixes match r_key +//trieģôдtrie, pat_trie_tag, trie_prefix_search_node_update> + +//Listmultimap/multiset +// +//ext/pb_ds/assoc_container.hppext/pb_ds/list_update_policy.hpp +//__gnu_pbds::list_update +//ģ +//template, +// typename Update_Policy = move_to_front_lu_policy<>, +// typename Allocator = std::allocator> +//class list_update; + +//ܽ +//priority_queueSTL֧modifyerasejoin +//tree൱STLеset/map֧splitjointree_order_statistics_node_updateֲ֧ѯrankkСֵԶNode_UpdateάϢ +//ĿǰУSTLûеhash_table +//pb_dsstd::set/map/priority_queueʹø