决定从头到尾干一波BZOJ!
可能会写没几题就停下吧,但还是想学学新姿势啦。

1001. [BeiJing2006]狼抓兔子

即求 $(1, 1)$ 到 $(n, m)$ 的最小割。跑 dinic 即可。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. inline int read() {
  5. int x = , f = ; char ch = getchar();
  6. while (ch < '' || ch > '') { if (ch == '-') f = -; ch = getchar(); }
  7. while (ch >= '' && ch <= '') { x = x * + ch - ; ch = getchar(); }
  8. return x * f;
  9. }
  10.  
  11. const int N = 1e6 + ;
  12. const int INF = 0x3f3f3f3f;
  13. struct E { int v, ne, f; } e[N * ];
  14. int head[N], cnt, n, m, iter[N], level[N];
  15.  
  16. inline void add(int u, int v, int f) {
  17. e[cnt].v = v; e[cnt].f = f; e[cnt].ne = head[u]; head[u] = cnt++;
  18. e[cnt].v = u; e[cnt].f = f; e[cnt].ne = head[v]; head[v] = cnt++;
  19. }
  20.  
  21. bool bfs(int s, int t) {
  22. for (int i = ; i <= t; i++) level[i] = -, iter[i] = head[i];
  23. queue<int> que;
  24. que.push(s);
  25. level[s] = ;
  26. while (!que.empty()) {
  27. int u = que.front(); que.pop();
  28. for (int i = head[u]; ~i; i = e[i].ne) {
  29. int v = e[i].v, f = e[i].f;
  30. if (level[v] < && f) {
  31. level[v] = level[u] + ;
  32. que.push(v);
  33. }
  34. }
  35. }
  36. return level[t] != -;
  37. }
  38.  
  39. int dfs(int u, int t, int f) {
  40. if (u == t || !f) return f;
  41. int flow = ;
  42. for (int i = iter[u]; ~i; i = e[i].ne) {
  43. iter[u] = i;
  44. int v = e[i].v;
  45. if (level[v] == level[u] + && e[i].f) {
  46. int w = dfs(v, t, min(f, e[i].f));
  47. if (!w) continue;
  48. e[i].f -= w, e[i^].f += w;
  49. flow += w, f -= w;
  50. if (f <= ) break;
  51. }
  52. }
  53. return flow;
  54. }
  55.  
  56. int main() {
  57. memset(head, -, sizeof(head));
  58. n = read(), m = read();
  59. for (int i = ; i <= n; i++) {
  60. for (int j = ; j < m; j++) {
  61. int f = read();
  62. add((i - ) * m + j, (i - ) * m + j + , f);
  63. }
  64. }
  65. for (int i = ; i < n; i++) {
  66. for (int j = ; j <= m; j++) {
  67. int f = read();
  68. add((i - ) * m + j, i * m + j, f);
  69. }
  70. }
  71. for (int i = ; i < n; i++) {
  72. for (int j = ; j < m; j++) {
  73. int f = read();
  74. add((i - ) * m + j, i * m + j + , f);
  75. }
  76. }
  77. int ans = ;
  78. int s = , t = n * m;
  79. for (; bfs(s, t); ans += dfs(s, t, INF));
  80. printf("%d\n", ans);
  81. return ;
  82. }

1002. [FJOI2007]轮状病毒
生成树计数。
基尔霍夫矩阵为度数矩阵减去邻接矩阵。
无向图生成树计数为基尔霍夫矩阵的行列式
可得递推方程
$ans = 3 \times f(n - 1) - 2 \times f(n - 2) - 2$
$f(n) = 3 \times f(n - 1) - f(n - 2)$
加上高精度即可。
注意算行列式时多写几行容易看。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. struct Bigi {
  5. int a[], len;
  6. Bigi() {
  7. memset(a, , sizeof(a));
  8. len = ;
  9. }
  10. friend Bigi operator * (int x, Bigi b) {
  11. Bigi res;
  12. res.len = b.len + ;
  13. for (int i = ; i <= res.len; i++) {
  14. res.a[i] += b.a[i] * x;
  15. res.a[i + ] += res.a[i] / ;
  16. res.a[i] %= ;
  17. }
  18. while (res.len > && res.a[res.len] == ) res.len--;
  19. return res;
  20. }
  21. friend Bigi operator - (Bigi x, Bigi y) {
  22. Bigi res;
  23. res.len = x.len;
  24. for (int i = ; i <= res.len; i++) {
  25. res.a[i] = x.a[i] - y.a[i];
  26. while (res.a[i] < ) {
  27. res.a[i] += ;
  28. x.a[i + ]--;
  29. }
  30. }
  31. while (res.len > && res.a[res.len] == ) res.len--;
  32. return res;
  33. }
  34. friend Bigi operator + (Bigi x, int y) {
  35. Bigi res = x;
  36. res.a[] += y;
  37. for (int i = ; i <= res.len; i++) {
  38. if (res.a[i] >= ) {
  39. res.a[i] -= ;
  40. res.a[i + ]++;
  41. } else {
  42. break;
  43. }
  44. }
  45. while (res.a[res.len + ]) res.len++;
  46. return res;
  47. }
  48. void print() {
  49. for (int i = len; i; i--)
  50. printf("%d", a[i]);
  51. puts("");
  52. }
  53. } big[], ans;
  54.  
  55. int main() {
  56. int n;
  57. scanf("%d", &n);
  58. big[] = big[] + ;
  59. big[] = big[] + ;
  60. if (n <= ) {
  61. big[n].print();
  62. return ;
  63. }
  64. for (int i = ; i <= n; i++)
  65. big[i] = * big[i - ] - big[i - ];
  66. ans = * big[n - ] - * big[n - ];
  67. ans = ans + (-);
  68. ans.print();
  69. return ;
  70. }

1003. [ZJOI2006]物流运输
$cost[i][j]$ 表示第 $i$ 天到第 $j$ 天都走同一条路线时每天的最小花费,即为 $1$ 到 $m$ 的最短路。dijkstra即可。
然后 $dp[i]$ 表示到第 $i$ 天的最小花费
$dp[i] = min(dp[j] + cost[j + 1][i] * (i - j) + k)$

  1. #include <bits/stdc++.h>
  2. #define pii pair<int, int>
  3. #define fi first
  4. #define se second
  5. using namespace std;
  6.  
  7. const int INF = ;
  8. const int N = ;
  9. int cost[N][N], dp[N], n, m, k, e;
  10. vector<pii> G[N];
  11. bool ban[N][N], unable[N], done[N];
  12. int dis[N];
  13.  
  14. inline void checkmin(int &a, int b) {
  15. if (a > b) a = b;
  16. }
  17.  
  18. int dijkstra(int l, int r) {
  19. for (int i = ; i <= m; i++) {
  20. unable[i] = ;
  21. done[i] = ;
  22. dis[i] = INF;
  23. for (int j = l; j <= r; j++)
  24. unable[i] |= ban[j][i];
  25. }
  26. if (unable[] || unable[m]) return INF;
  27. priority_queue<pii, vector<pii>, greater<pii> > que;
  28. dis[] = ;
  29. que.push(pii(, ));
  30. while (!que.empty()) {
  31. auto pp = que.top(); que.pop();
  32. int u = pp.se;
  33. if (done[u]) continue;
  34. done[u] = ;
  35. for (auto p: G[u]) {
  36. int v = p.se, c = p.fi;
  37. if (!unable[v] && dis[v] > dis[u] + c) {
  38. dis[v] = dis[u] + c;
  39. que.push(pii(dis[v], v));
  40. }
  41. }
  42. }
  43. return dis[m];
  44. }
  45.  
  46. int main() {
  47. //freopen("in.txt", "r", stdin);
  48. scanf("%d%d%d%d", &n, &m, &k, &e);
  49. for (int u, v, c; e--; ) {
  50. scanf("%d%d%d", &u, &v, &c);
  51. G[u].push_back(pii(c, v));
  52. G[v].push_back(pii(c, u));
  53. }
  54. int q;
  55. scanf("%d", &q);
  56. for (int u, a, b; q--; ) {
  57. scanf("%d%d%d", &u, &a, &b);
  58. for (int i = a; i <= b; i++)
  59. ban[i][u] = ;
  60. }
  61. for (int i = ; i <= n; i++)
  62. for (int j = ; j <= n; j++)
  63. cost[i][j] = dijkstra(i, j);
  64. for (int i = ; i <= n; i++) {
  65. dp[i] = cost[][i] * i;
  66. for (int j = ; j < i; j++)
  67. checkmin(dp[i], dp[j] + cost[j + ][i] * (i - j) + k);
  68. }
  69. printf("%d\n", dp[n]);
  70. return ;
  71. }

1005. [HNOI2008]明明的烦恼
prufer序列为无根树的一种数列。长度为 $n$ - $2$
prufer转无根树
将最小编号的叶子删去,prufer序列加入其父亲。重复至树只剩下两个节点。
无根树转prufer
取出prufer首元素,与待选点集中最小未出现在prufer序列中的点连边,并将该点在待选点集中删去,直至待选点集剩下两个节点,将这两个节点连边。待选点集初始为 $1$ ~ $n$。
一个节点在prufer序列中出现次数为该节点度数减一。
判断无解的情况:出现度数为 $0$ 的点,在prufer序列中出现次数超过 $n$ - $2$。
有解情况下,设 $cnt$ 为有度数要求的节点个数,$sum = \sum_{i = 1} ^{cnt}(d_i - 1)$。
那么答案为 $C_{n-2}^{sum} \times \dfrac{sum!}{\prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$
化简得到$\dfrac{(n-2)!}{(n-sum-2)! \times \prod_{i=1}^{cnt}(d_i-1)!} \times (n-cnt)^{n-2-sum}$

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. const int MOD = ;
  5. int num[N];
  6. int prime[N], tol, d[N], c[N];
  7. bool vis[N];
  8.  
  9. void init() {
  10. for (int i = ; i < N; i++) {
  11. if (!vis[i]) prime[++tol] = i;
  12. for (int j = ; j <= tol && i * prime[j] < N; j++) {
  13. vis[i * prime[j]] = ;
  14. if (i % prime[j] == ) break;
  15. }
  16. }
  17. }
  18.  
  19. void add(int x, int o) {
  20. for (int i = ; i <= tol; i++) {
  21. while (x % prime[i] == )
  22. c[i] += o, x /= prime[i];
  23. }
  24. }
  25.  
  26. int main() {
  27. init();
  28. int n;
  29. scanf("%d", &n);
  30. bool flag = ;
  31. int sum = , cnt = ;
  32. for (int i = ; i <= n; i++) {
  33. scanf("%d", d + i);
  34. if (!d[i] || d[i] > n - ) flag = ;
  35. if (d[i] != -) sum += d[i] - , cnt++;
  36. }
  37. if (n == ) {
  38. if (!d[]) puts("");
  39. else puts("");
  40. return ;
  41. }
  42. if (sum > n - || flag) {
  43. puts("");
  44. return ;
  45. }
  46. for (int i = n - - sum + ; i <= n - ; i++)
  47. add(i, );
  48. for (int i = ; i <= n; i++) {
  49. if (d[i] > -) {
  50. for (int j = ; j < d[i]; j++)
  51. add(j, -);
  52. }
  53. }
  54. int len = ;
  55. num[++len] = ;
  56. for (int i = ; i <= n - - sum; i++) {
  57. for (int j = ; j <= len; j++) num[j] *= n - cnt;
  58. for (int j = ; j <= len; j++) {
  59. if (num[j] >= MOD) {
  60. num[j + ] += num[j] / MOD;
  61. num[j] %= MOD;
  62. }
  63. }
  64. while (num[len + ]) {
  65. num[len + ] += num[len] / MOD;
  66. num[len] %= MOD;
  67. len++;
  68. }
  69. }
  70. for (int i = ; i <= tol; i++) {
  71. while (c[i]) {
  72. for (int j = ; j <= len; j++) num[j] *= prime[i];
  73. for (int j = ; j <= len; j++) {
  74. if (num[j] >= MOD) {
  75. num[j + ] += num[j] / MOD;
  76. num[j] %= MOD;
  77. }
  78. }
  79. while (num[len + ]) {
  80. num[len + ] += num[len] / MOD;
  81. num[len] %= MOD;
  82. len++;
  83. }
  84. c[i]--;
  85. }
  86. }
  87. printf("%d", num[len]);
  88. for (int i = len - ; i; i--) printf("%04d", num[i]);
  89. puts("");
  90. return ;
  91. }

1007. [HNOI2008]水平可见直线
可见的直线为一下凸壳。
先按斜率和截距从小到大排序,再用单调栈判断交点的相对位置即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 5e4 + ;
  4. const double eps = 1e-;
  5.  
  6. inline int dcmp(double x) {
  7. if (fabs(x) < eps) return ;
  8. return x < ? - : ;
  9. }
  10.  
  11. struct P {
  12. double x, y;
  13. int id;
  14. inline bool operator < (const P &rhs) const {
  15. if (dcmp(x - rhs.x) == ) return y < rhs.y;
  16. return x < rhs.x;
  17. }
  18. } p[N];
  19.  
  20. int st[N], top;
  21.  
  22. inline double crossx(const P &a, const P &b) {
  23. return (a.y - b.y) / (b.x - a.x);
  24. }
  25.  
  26. void ins(int id) {
  27. const P &cur = p[id];
  28. while (top) {
  29. if (dcmp(p[st[top]].x - cur.x) == ) top--;
  30. else if (top > && dcmp(crossx(p[st[top]], cur) - crossx(p[st[top]], p[st[top - ]])) <= ) top--;
  31. else break;
  32. }
  33. st[++top] = id;
  34. }
  35.  
  36. int ans[N];
  37.  
  38. int main() {
  39. int n;
  40. scanf("%d", &n);
  41. for (int i = ; i <= n; i++) {
  42. scanf("%lf%lf", &p[i].x, &p[i].y);
  43. p[i].id = i;
  44. }
  45. std::sort(p + , p + + n);
  46. for (int i = ; i <= n; i++)
  47. ins(i);
  48. for (int i = ; i <= top; i++)
  49. ans[p[st[i]].id] = ;
  50. for (int i = ; i <= n; i++)
  51. if (ans[i])
  52. printf("%d ", i);
  53. return ;
  54. }

1008. [HNOI2008]越狱
总方案数 - 相邻颜色均不同的方案数。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int MOD = ;
  5.  
  6. int qp(int a, ll b) {
  7. a %= MOD;
  8. int ans = ;
  9. while (b) {
  10. if (b & ) ans = 1LL * ans * a % MOD;
  11. a = 1LL * a * a % MOD;
  12. b >>= ;
  13. }
  14. return ans;
  15. }
  16.  
  17. int main() {
  18. int m;
  19. ll n;
  20. scanf("%d%lld", &m, &n);
  21. int ans = qp(m, n);
  22. ans = (ans - 1LL * m * qp(m - , n - ) % MOD) % MOD;
  23. ans = (ans + MOD) % MOD;
  24. printf("%d\n", ans);
  25. return ;
  26. }

1009. [HNOI2008]GT考试

显然有一个DP方程 $dp[i][j]$ 表示到第 $i$ 位已经末尾匹配了 $j$ 位的方案数。

暴力的话就枚举下一位放啥,看放完之后又匹配了多少。

这里可以引入一个 $f[i][j]$ 数组表示从不吉利数字当前匹配了 $i$ 位,加上一个字符能匹配 $j$ 位的方案数。

这一部分可以用kmp得到。

然后dp的转移方程即为 $dp[i][j] = \sum dp[i - 1][k] \times f[k][j]$

答案为 $\sum_{i = 0}^{m - 1} dp[n][i]$

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. int MOD, n, m;
  5. char s[N];
  6. int ne[N];
  7.  
  8. struct Mat {
  9. int mat[][];
  10. Mat() {
  11. memset(mat, , sizeof mat);
  12. }
  13. Mat operator * (const Mat &rhs) const {
  14. Mat c;
  15. for (int i = ; i < n; i++)
  16. for (int j = ; j < n; j++)
  17. for (int k = ; k < n; k++)
  18. (c.mat[i][j] += mat[i][k] * rhs.mat[k][j]) %= MOD;
  19. return c;
  20. }
  21. };
  22.  
  23. Mat qp(Mat ans, Mat a, int b) {
  24. while (b) {
  25. if (b & ) ans = ans * a;
  26. a = a * a;
  27. b >>= ;
  28. }
  29. return ans;
  30. }
  31.  
  32. void kmp() {
  33. int i = , j = ne[] = -;
  34. while (i < n) {
  35. while (j != - && s[i] != s[j]) j = ne[j];
  36. ne[++i] = ++j;
  37. }
  38. }
  39.  
  40. int main() {
  41. scanf("%d%d%d", &m, &n, &MOD);
  42. scanf("%s", s);
  43. kmp();
  44. Mat b;
  45. for (int i = ; i < n; i++) {
  46. for (int j = ; j < ; j++) {
  47. int k = i;
  48. while (k != - && s[k] != j + '') k = ne[k];
  49. k++;
  50. if (k < n) b.mat[i][k]++;
  51. }
  52. }
  53. Mat a;
  54. a.mat[][] = ;
  55. a = qp(a, b, m);
  56. int ans = ;
  57. for (int i = ; i < n; i++)
  58. ans += a.mat[][i];
  59. ans %= MOD;
  60. printf("%d\n", ans);
  61. return ;
  62. }

1010. [HNOI2008]玩具装箱toy

$dp[i]$ 表示以 $i$ 物品为结尾的最小费用

$dp[i] = min(dp[j] + (j - i + \sum c_k - l)^2)$

斜率优化一下即可。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4.  
  5. const int N = 5e4 + ;
  6. ll sum[N], l, c[N], dp[N];
  7. int que[N], n;
  8.  
  9. template <class T>
  10. inline T sqr (T a) {
  11. return a * a;
  12. }
  13.  
  14. inline ll X(int i) {
  15. return sum[i];
  16. }
  17.  
  18. inline ll Y(int i) {
  19. return dp[i] + sqr(sum[i]) + * l * sum[i];
  20. }
  21.  
  22. inline double K(int i, int j) {
  23. return 1.0 * (Y(i) - Y(j)) / (X(i) - X(j));
  24. }
  25.  
  26. int main() {
  27. scanf("%d%lld", &n, &l);
  28. for (int i = ; i <= n; i++)
  29. scanf("%lld", &c[i]), sum[i] = sum[i - ] + c[i];
  30. for (int i = ; i <= n; i++)
  31. sum[i] += i;
  32. int head = , tail = ;
  33. l++;
  34. for (int i = ; i <= n; i++) {
  35. while (head < tail && K(que[head], que[head + ]) < * sum[i]) head++;
  36. int j = que[head];
  37. dp[i] = dp[j] + sqr(sum[i] - sum[j] - l);
  38. while (head < tail && K(que[tail - ], que[tail]) > K(que[tail], i)) tail--;
  39. que[++tail] = i;
  40. }
  41. printf("%lld\n", dp[n]);
  42. return ;
  43. }

1011. [HNOI2008]遥远的行星

怀疑是没有spj。反正是个瞎搞题?

1012. [JSOI2008]最大数maxnumber

开足够位置就变成单点修改区间查询。

线段树维护即可。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = 2e5 + ;
  5.  
  6. struct Seg {
  7. #define lp p << 1
  8. #define rp p << 1 | 1
  9. ll tree[N << ];
  10. void pushup(int p) {
  11. tree[p] = std::max(tree[lp], tree[rp]);
  12. }
  13. void update(int p, int l, int r, int pos, ll v) {
  14. if (l == r) {
  15. tree[p] = v;
  16. return;
  17. }
  18. int mid = l + r >> ;
  19. if (pos <= mid) update(lp, l, mid, pos, v);
  20. else update(rp, mid + , r, pos, v);
  21. pushup(p);
  22. }
  23. ll query(int p, int l, int r, int x, int y) {
  24. if (x <= l && y >= r) return tree[p];
  25. int mid = l + r >> ;
  26. ll ans = ;
  27. if (x <= mid) ans = std::max(ans, query(lp, l, mid, x, y));
  28. if (y > mid) ans = std::max(ans, query(rp, mid + , r, x, y));
  29. return ans;
  30. }
  31. } seg;
  32.  
  33. int main() {
  34. int n;
  35. ll D;
  36. scanf("%d%lld", &n, &D);
  37. ll ans = ;
  38. int cnt = ;
  39. for (int i = ; i <= n; i++) {
  40. char s[];
  41. ll num;
  42. scanf("%s%lld", s, &num);
  43. if (s[] == 'A') {
  44. num += ans;
  45. num %= D;
  46. seg.update(, , n, ++cnt, num);
  47. } else {
  48. printf("%lld\n", ans = seg.query(, , n, cnt - num + , cnt));
  49. }
  50. }
  51. return ;
  52. }

1013. [JSOI2008]球形空间产生器sphere

圆心坐标为 $(a_1, a_2, a_3, \cdots, a_n)$

那么可以列出 $n$ 条等式,两边的平方项都消掉了。那么高斯消元求解即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. const double eps = 1e-;
  5. int n;
  6. double a[N][N];
  7. struct Node {
  8. double d[N];
  9. } p[N];
  10.  
  11. void gauss() {
  12. for (int i = ; i <= n; i++) {
  13. int r = i;
  14. for (int j = i + ; j <= n; j++) {
  15. if (std::fabs(a[r][i]) < std::fabs(a[j][i]))
  16. r = j;
  17. }
  18. if (r != i) std::swap(a[i], a[r]);
  19. for (int j = ; j <= n; j++) {
  20. if (j != i) {
  21. double t = a[j][i] / a[i][i];
  22. for (int k = i; k <= n + ; k++)
  23. a[j][k] -= a[i][k] * t;
  24. }
  25. }
  26. }
  27. for (int i = ; i <= n; i++)
  28. a[i][n + ] /= a[i][i];
  29. }
  30.  
  31. int main() {
  32. scanf("%d", &n);
  33. for (int i = ; i <= n + ; i++)
  34. for (int j = ; j <= n; j++)
  35. scanf("%lf", &p[i].d[j]);
  36. for (int i = ; i <= n; i++) {
  37. for (int j = ; j <= n; j++) {
  38. a[i][j] = * p[i].d[j] - * p[i + ].d[j];
  39. a[i][n + ] += p[i].d[j] * p[i].d[j] - p[i + ].d[j] * p[i + ].d[j];
  40. }
  41. }
  42. gauss();
  43. for (int i = ; i <= n; i++)
  44. printf("%.3f%c", a[i][n + ], " \n"[i == n]);
  45. return ;
  46. }

1014. [JSOI2008]火星人prefix

如果没有插入操作,仅仅只有修改操作,那么可以用线段树维护区间哈希值,对于一个查询,二分长度,再判是否相等即可。

现在多了插入操作,那么就用 splay 来维护哈希值,与线段树不同的只是多了非叶子节点也代表一个字符。

  1. #include <bits/stdc++.h>
  2. #define ull unsigned long long
  3.  
  4. const int N = 2e5 + ;
  5. ull base[N];
  6. const ull BASE = ;
  7. int tol, root, m;
  8. char s[N];
  9.  
  10. struct Splay {
  11. int ch[N][], fa[N], sz[N];
  12. ull val[N], ha[N];
  13. inline bool chk(int x) {
  14. return ch[fa[x]][] == x;
  15. }
  16. inline void pushup(int x) {
  17. sz[x] = sz[ch[x][]] + sz[ch[x][]] + ;
  18. ha[x] = ha[ch[x][]] + val[x] * base[sz[ch[x][]]] + ha[ch[x][]] * base[(sz[ch[x][]] + )];
  19. }
  20. void rotate(int x) {
  21. int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k ^ ];
  22. ch[y][k] = w; fa[w] = y;
  23. ch[z][chk(y)] = x; fa[x] = z;
  24. ch[x][k ^ ] = y; fa[y] = x;
  25. pushup(y); pushup(x);
  26. }
  27. void splay(int x, int goal = ) {
  28. while (fa[x] != goal) {
  29. int y = fa[x], z = fa[y];
  30. if (z != goal)
  31. rotate((chk(x) == chk(y)) ? y : x);
  32. rotate(x);
  33. }
  34. pushup(x);
  35. if (!goal) root = x;
  36. pushup(x);
  37. }
  38. int kth(int k) {
  39. int cur = root;
  40. while () {
  41. if (ch[cur][] && k <= sz[ch[cur][]])
  42. cur = ch[cur][];
  43. else if (k > + sz[ch[cur][]])
  44. k -= + sz[ch[cur][]], cur = ch[cur][];
  45. else
  46. break;
  47. }
  48. return cur;
  49. }
  50. ull gethash(int l, int len) {
  51. int x = kth(l), y = kth(l + len + );
  52. splay(x), splay(y, x);
  53. return ha[ch[y][]];
  54. }
  55. void insert(int x, int v) {
  56. int l = kth(x + ), r = kth(x + );
  57. splay(l); splay(r, l);
  58. val[++tol] = v; fa[tol] = r; ch[r][] = tol;
  59. splay(tol);
  60. }
  61. void update(int x, int v) {
  62. int l = kth(x), r = kth(x + );
  63. splay(l); splay(r, l);
  64. val[ch[r][]] = v;
  65. splay(ch[r][]);
  66. }
  67. int query(int x, int y) {
  68. int l = , r = tol - std::max(x, y) - ;
  69. int ans = ;
  70. while (l <= r) {
  71. int mid = l + r >> ;
  72. if (gethash(x, mid) == gethash(y, mid)) ans = mid, l = mid + ;
  73. else r = mid - ;
  74. }
  75. return ans;
  76. }
  77. void init() {
  78. ch[][] = ;
  79. root = , tol = ;
  80. fa[] = ;
  81. pushup();
  82. pushup();
  83. }
  84. } splay;
  85.  
  86. int main() {
  87. for (int i = base[] = ; i < N; i++)
  88. base[i] = base[i - ] * BASE;
  89. scanf("%s%d", s + , &m);
  90. int n = strlen(s + );
  91. splay.init();
  92. for (int i = ; i <= n; i++)
  93. splay.insert(i - , s[i]);
  94. for (int x, y; m--; ) {
  95. char s[];
  96. scanf("%s", s);
  97. if (s[] == 'Q') {
  98. scanf("%d%d", &x, &y);
  99. printf("%d\n", splay.query(x, y));
  100. } else if (s[] == 'R') {
  101. char ss[];
  102. scanf("%d%s", &x, s);
  103. splay.update(x, s[]);
  104. } else {
  105. char ss[];
  106. scanf("%d%s", &x, s);
  107. splay.insert(x, s[]);
  108. }
  109. }
  110. return ;
  111. }

1015. [JSOI2008]星球大战starwar

并查集不好直接删除边,那么考虑时光倒流,反向加边即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 4e5 + ;
  4.  
  5. struct Edge {
  6. int x, y;
  7. } e[N];
  8. std::vector<int> vec[N];
  9.  
  10. int n, m, fa[N], a[N], res, ans[N];
  11. bool vis[N];
  12.  
  13. int getfa(int x) {
  14. return x == fa[x] ? x : fa[x] = getfa(fa[x]);
  15. }
  16.  
  17. void merge(int x, int y) {
  18. x = getfa(x), y = getfa(y);
  19. if (x != y) {
  20. fa[x] = y;
  21. res--;
  22. }
  23. }
  24.  
  25. int main() {
  26. scanf("%d%d", &n, &m);
  27. for (int i = ; i < n; i++)
  28. fa[i] = i;
  29. for (int i = ; i < m; i++)
  30. scanf("%d%d", &e[i].x, &e[i].y), vec[e[i].x].push_back(e[i].y), vec[e[i].y].push_back(e[i].x);
  31. int k;
  32. scanf("%d", &k);
  33. for (int i = ; i <= k; i++)
  34. scanf("%d", a + i), vis[a[i]] = ;
  35. res = n - k;
  36. for (int i = ; i < m; i++)
  37. if (!vis[e[i].x] && !vis[e[i].y])
  38. merge(e[i].x, e[i].y);
  39. for (int i = k; i; i--) {
  40. ans[i] = res;
  41. int u = a[i];
  42. vis[u] = ;
  43. res++;
  44. for (int v: vec[u])
  45. if (!vis[v])
  46. merge(u, v);
  47. }
  48. printf("%d\n", res);
  49. for (int i = ; i <= k; i++)
  50. printf("%d\n", ans[i]);
  51. return ;
  52. }

1016. [JSOI2008]最小生成树计数

最小生成树会出现多个是因为权值相同的边可替换。把处理同权值的边称为一个阶段,若处理一个阶段中的边的顺序会影响该阶段后连通块的连通性,那么这就与可替换相矛盾。

所以处理完一个阶段后这棵树的连通性应该是固定的。(感觉解释不太清楚,但是应该可以意会吧)。

用并查集 $fa$ 和 邻接矩阵 $G$ 表示每一时刻树的连通情况以及邻接情况。

用并查集 $pre$ 表示上一阶段树的连通情况。

处理一个阶段后,对每个连通块用矩阵树定理求一遍生成树个数,根据乘法原理相乘。

然后再把连通块缩成点再处理下一阶段。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. const int N = ;
  5. const int MOD = ;
  6.  
  7. struct E {
  8. int u, v, cost;
  9. inline bool operator < (const E &rhs) const {
  10. return cost < rhs.cost;
  11. }
  12. void read() {
  13. scanf("%d%d%d", &u, &v, &cost);
  14. }
  15. } edge[N * ];
  16.  
  17. int getfa(int x, int *fa) {
  18. return x == fa[x] ? x : fa[x] = getfa(fa[x], fa);
  19. }
  20.  
  21. int Gauss(int a[N][N], int n) {
  22. int ans = ;
  23. for (int i = ; i <= n; i++) {
  24. for (int k = i + ; k <= n; ++k) {
  25. while (a[k][i]) {
  26. int d = a[i][i] / a[k][i];
  27. for (int j = i; j <= n; j++)
  28. a[i][j] = (a[i][j] - d * a[k][j] + MOD) % MOD;
  29. swap(a[i], a[k]);
  30. ans = -ans;
  31. }
  32. }
  33. ans = 1LL * ans * a[i][i] % MOD;
  34. ans = (ans + MOD) % MOD;
  35. }
  36. return ans;
  37. }
  38.  
  39. int A[N][N], G[N][N], fa[N], pre[N];
  40. vector<int> vec[N];
  41. bool vis[N];
  42.  
  43. int main() {
  44. int n, m;
  45. scanf("%d%d", &n, &m);
  46. for (int i = ; i < m; i++)
  47. edge[i].read();
  48. sort(edge, edge + m);
  49. for (int i = ; i <= n; i++) pre[i] = i;
  50. int last = -;
  51. int ans = ;
  52. for (int k = ; k <= m; k++) {
  53. if (last != edge[k].cost || k == m) {
  54. for (int i = ; i <= n; i++) {
  55. if (vis[i]) {
  56. int u = getfa(i, fa);
  57. vec[u].push_back(i);
  58. vis[i] = ;
  59. }
  60. }
  61. for (int i = ; i <= n; i++) {
  62. if (vec[i].size() > ) {
  63. memset(A, , sizeof(A));
  64. int len = vec[i].size();
  65. for (int a = ; a < len; a++)
  66. for (int b = a + ; b < len; b++) {
  67. int u = vec[i][a], v = vec[i][b];
  68. A[a][b] = A[b][a] = -G[u][v];
  69. A[a][a] += G[u][v];
  70. A[b][b] += G[u][v];
  71. }
  72. ans = ans * Gauss(A, len - ) % MOD;
  73. for (int a = ; a < len; a++)
  74. pre[vec[i][a]] = i;
  75. }
  76. }
  77. for (int i = ; i <= n; i++)
  78. fa[i] = getfa(i, pre), vec[i].clear();
  79. if (k == m) break;
  80. last = edge[k].cost;
  81. }
  82. int u = edge[k].u, v = edge[k].v;
  83. u = getfa(u, pre); v = getfa(v, pre);
  84. if (u == v) continue;
  85. vis[u] = vis[v] = ;
  86. fa[getfa(u, fa)] = getfa(v, fa);
  87. G[u][v]++;
  88. G[v][u]++;
  89. }
  90. int flag = ;
  91. for (int i = ; i <= n; i++) {
  92. if (getfa(i, fa) != getfa(i - , fa)) {
  93. puts("");
  94. return ;
  95. }
  96. }
  97. if (!m) {
  98. puts("");
  99. return ;
  100. }
  101. printf("%d\n", ans);
  102. return ;
  103. }

1017. [JSOI2008]魔兽地图DotR

一种很妙的树上背包。

$dp[i][j][k]$ 表示处理完 $i$ 的子树,$j$ 个 $i$ 物品贡献给父亲,花费为 $k$ 的最大价值。

再用一个 $f[i][j]$ 表示做完 $i$ 个儿子,花费为 $j$ 的最大价值

然后就可以xjb转移了。

  1. #include <bits/stdc++.h>
  2. #define pii pair<int, int>
  3. #define fi first
  4. #define se second
  5.  
  6. const int N = ;
  7. const int INF = 0x3f3f3f3f;
  8. const int M = 2e3 + ;
  9.  
  10. int dp[N][][M], f[N][M], n, m, mx[N], cost[N], val[N];
  11. int degree[N];
  12. std::vector<std::pii> vec[N];
  13.  
  14. void dfs(int u) {
  15. if (vec[u].empty()) {
  16. mx[u] = std::min(mx[u], m / cost[u]);
  17. for (int i = ; i <= mx[u]; i++)
  18. for (int j = i; j <= mx[u]; j++)
  19. dp[u][i][j * cost[u]] = (j - i) * val[u];
  20. return;
  21. }
  22. mx[u] = INF;
  23. for (auto p: vec[u]) {
  24. int v = p.fi;
  25. dfs(v);
  26. mx[u] = std::min(mx[u], mx[v] / p.se);
  27. cost[u] += cost[v] * p.se;
  28. }
  29. mx[u] = std::min(mx[u], m / cost[u]);
  30. memset(f, 0xcf, sizeof f);
  31. f[][] = ;
  32. for (int c = mx[u]; ~c; c--) {
  33. int cur = ;
  34. for (auto p: vec[u]) {
  35. int v = p.fi;
  36. cur++;
  37. for (int j = ; j <= m; j++)
  38. for (int k = ; k <= j; k++)
  39. f[cur][j] = std::max(f[cur][j], f[cur - ][j - k] + dp[v][c * p.se][k]);
  40. }
  41. for (int i = ; i <= c; i++)
  42. for (int j = ; j <= m; j++)
  43. dp[u][i][j] = std::max(dp[u][i][j], (c - i) * val[u] + f[cur][j]);
  44. }
  45. }
  46.  
  47. int main() {
  48. scanf("%d%d", &n, &m);
  49. for (int i = ; i <= n; i++) {
  50. scanf("%d", val + i);
  51. char s[];
  52. scanf("%s", s);
  53. if (s[] == 'A') {
  54. int cnt;
  55. scanf("%d", &cnt);
  56. while (cnt--) {
  57. int who, need;
  58. scanf("%d%d", &who, &need);
  59. vec[i].push_back(std::pii(who, need));
  60. degree[who]++;
  61. }
  62. } else {
  63. scanf("%d%d", cost + i, mx + i);
  64. }
  65. }
  66. bool flag = ;
  67. for (int i = ; i <= n; i++)
  68. if (degree[i])
  69. flag = ;
  70. int ans = ;
  71. memset(dp, 0xcf, sizeof dp);
  72. if (flag) {
  73. for (int i = ; i <= n; i++)
  74. if (!degree[i]) {
  75. dfs(i);
  76. for (int j = ; j <= mx[i]; j++)
  77. for (int k = ; k <= m; k++)
  78. ans = std::max(ans, dp[i][j][k]);
  79. }
  80. } else {
  81. f[][] = ;
  82. for (int i = ; i <= n; i++)
  83. for (int k = ; k <= mx[i]; k++)
  84. for (int j = k * cost[i]; j <= m; j++)
  85. f[i][j] = std::max(f[i][j], f[i - ][j - k * cost[i]] + k * val[i]);
  86. for (int i = ; i <= m; i++)
  87. ans = std::max(ans, f[n][i]);
  88. }
  89. printf("%d\n", ans);
  90. return ;
  91. }

1018. [SHOI2008]堵塞的交通traffic

线段树分治。具体就是能实现删除/撤销操作。把时间看成下标,那么加入和删除就是一个区间,就像线段树一样分成 $log$ 个区间去操作。

可撤回的并查集就得按秩合并实现了。复杂度为 $O(nlog^2 n)$。

  1. #include <bits/stdc++.h>
  2. #define pii pair<int, int>
  3. #define fi first
  4. #define se second
  5.  
  6. const int N = 2e5 + ;
  7. int fa[N], sz[N], top, n, q, cnt, id[][N], tol;
  8. std::pii st[N], query[N];
  9. std::map<int, int> mp[N];
  10.  
  11. struct Node {
  12. int u, v, st, ed;
  13. Node(int u = , int v = , int st = , int ed = ): u(u), v(v), st(st), ed(ed) {}
  14. };
  15. std::vector<Node> E;
  16.  
  17. int getfa(int x) {
  18. while (fa[x] != x)
  19. x = fa[x];
  20. return x;
  21. }
  22.  
  23. inline void merge(int x, int y) {
  24. x = getfa(x), y = getfa(y);
  25. if (sz[x] > sz[y]) std::swap(x, y);
  26. fa[x] = y; sz[y] += sz[x];
  27. st[++top] = std::pii(x, y);
  28. }
  29.  
  30. void solve(int l, int r, const std::vector<Node> &E) {
  31. std::vector<Node> L, R;
  32. int mid = l + r >> ;
  33. int temp = top;
  34. for (auto p: E) {
  35. if (p.st <= l && p.ed >= r) {
  36. merge(p.u, p.v);
  37. } else {
  38. if (p.st <= mid) L.push_back(p);
  39. if (p.ed > mid) R.push_back(p);
  40. }
  41. }
  42. if (l == r) {
  43. puts(getfa(query[l].fi) == getfa(query[l].se) ? "Y" : "N");
  44. } else {
  45. solve(l, mid, L);
  46. solve(mid + , r, R);
  47. }
  48. while (top > temp) {
  49. int x = st[top].fi, y = st[top].se;
  50. fa[x] = x; sz[y] -= sz[x];
  51. top--;
  52. }
  53. }
  54.  
  55. int main() {
  56. scanf("%d", &n);
  57. for (int i = ; i <= n; i++)
  58. id[][i] = ++tol, id[][i] = ++tol;
  59. while () {
  60. char s[];
  61. scanf("%s", s);
  62. if (s[] == 'E') break;
  63. int x1, y1, x2, y2;
  64. scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
  65. if (s[] == 'O') {
  66. mp[id[x1][y1]][id[x2][y2]] = mp[id[x2][y2]][id[x1][y1]] = E.size();
  67. E.push_back(Node(id[x1][y1], id[x2][y2], q + , -));
  68. } else if (s[] == 'C') {
  69. E[mp[id[x1][y1]][id[x2][y2]]].ed = q;
  70. } else {
  71. query[++q] = std::pii(id[x1][y1], id[x2][y2]);
  72. }
  73. }
  74. for (auto &p: E) {
  75. if (p.ed == -)
  76. p.ed = q;
  77. }
  78. for (int i = ; i <= tol; i++)
  79. fa[i] = i, sz[i] = ;
  80. solve(, q, E);
  81. return ;
  82. }

1019. [SHOI2008]汉诺塔

%这篇题解

会想到这个解法大概就是汉诺塔问题都是基于递归解决的思想吧,我要移动 $j$ 个盘子,那么就得先移动 $j-1$ 个盘子到一个柱子上,递归解决。

用 $f[j][i]$ 表示 $i$ 上有 $j$ 个盘子,其他柱子上没有盘子,将这 $j$ 个盘子移动到 $g[j][i]$ 上的最小步数

边界 $f[1][i] = 1$,$g[1][i]$ 由输入的优先级决定。

当前 $i$ 柱子上有 $j$ 个盘子,那么就先将 $j - 1$ 个盘子移动到 $a$ 上,$a=g[j - 1][i]$,剩下的柱子 $b = 3 - i - a$。再把最后一个盘子移动到 $b$ 上。

若 $g[j - 1][a] = b$,那么直接把 $a$ 上 $j - 1$ 个盘子移动到 $b$ 上就完成了。

此时 $f[j][i] = f[j - 1][i] + 1 + f[j - 1][a]$,$g[j][i] = b$。

否则 $g[j -1][a] = i$,那么先把 $a$ 上 $j-1$ 个盘子移动到 $i$ 上,再把 $b$ 上的大盘子移动到 $a$ 上,再把 $i$ 上 $j-1$ 个盘子移动到 $a$ 上。

此时 $f[j][i] = f[j - 1][i] + 1 + f[j - 1][a] + 1 + f[j - 1][i]$,$g[j][i] = a$。

答案即为 $f[n][0]$。

因为第一步符合操作优先级,那么第二步是模仿第一步来的,肯定也符合优先级,这么推下去也是符合优先级的。

然后又有一个要求是不移动刚移动的那个盘子,我们在移动完 $f[j - 1][i]$ 或者 $f[j - 1][a]$ 之后,都是移动那个大盘子,而不会再移动这 $j-1$ 个盘子(其实也就是最顶部那个盘子),所以也是符合要求的。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = ;
  5. ll f[N][];
  6. int g[N][];
  7. bool vis[];
  8.  
  9. int main() {
  10. int n;
  11. scanf("%d", &n);
  12. for (int i = ; i <= ; i++) {
  13. static char s[];
  14. scanf("%s", s);
  15. int a = s[] - 'A', b = s[] - 'A';
  16. if (vis[a]) continue;
  17. vis[a] = ;
  18. f[][a] = ;
  19. g[][a] = b;
  20. }
  21. for (int j = ; j <= n; j++)
  22. for (int i = ; i < ; i++) {
  23. int a = g[j - ][i], b = - a - i;
  24. if (g[j - ][a] == b) f[j][i] = f[j - ][i] + + f[j - ][a], g[j][i] = b;
  25. else f[j][i] = f[j - ][i] + + f[j - ][a] + + f[j - ][i], g[j][i] = a;
  26. }
  27. printf("%lld\n", f[n][]);
  28. return ;
  29. }

1020. [SHOI2008]安全的航线flight

可以对一条线段每间隔一个 eps 的长度的点找一下答案,但是这样会TLE。

其实得到一个答案后,很多小于这个答案的点就可以不用搜了。

可以迭代地解决问题。

先把所有航线的线段加入一个队列。

1. 取出一条线段后,左端点为 $a$,右端点为 $b$,找到 $a$ 离得最近的陆地的点 $p_1$,并用两者的距离更新答案,找到 $b$ 离的最近的陆地的点 $p_2$,并用两者的距离更新答案。

2. 找到线段 $ab$ 上的点 $p$,使得 $p$ 和 $p_1$ 之间的距离与 $p$ 和 $p_2$ 之间的距离 $d$ 大致相等,这一步可以二分得到。

3. 当 $d$ 的距离比当前 ans 小的时候,就丢掉这条线段。

4. 否则将 $(a, p)$ 和 $(p, b)$ 重新加入队列。

5. 重复上述过程直到队列为空。

第 3 步就是一个最优性剪枝,因为这条线段上的所有点到最近陆地的距离都不超过 $d$。

  1. #include <bits/stdc++.h>
  2.  
  3. const double pi = acos(-1.0);
  4. const int INF = 0x3f3f3f3f;
  5. const double eps = 1e-;
  6. const int N = ;
  7.  
  8. int dcmp(double x) {
  9. if (fabs(x) < eps) return ;
  10. return x > ? : -;
  11. }
  12.  
  13. struct Point {
  14. double x, y;
  15. Point(double x = , double y = ): x(x), y(y) {}
  16. Point operator + (const Point &p) const { return Point(x + p.x, y + p.y); }
  17. Point operator - (const Point &p) const { return Point(x - p.x, y - p.y); }
  18. Point operator * (const double &rhs) const { return Point(x * rhs, y * rhs); }
  19. Point operator / (const double &rhs) const { return Point(x / rhs, y / rhs); }
  20. bool operator == (const Point &p) const { return !dcmp(x - p.x) && !dcmp(y - p.y); }
  21. void read() { scanf("%lf%lf", &x, &y); }
  22. void print() { printf("%.6f %.6f\n", x, y); }
  23. } flight[N];
  24.  
  25. typedef Point Vector;
  26.  
  27. double Dot(const Vector &a, const Vector &b) { return a.x * b.x + a.y * b.y; }
  28. double Len(const Vector &a) { return sqrt(Dot(a, a)); }
  29. double Cross(const Vector &a, const Vector &b) { return a.x * b.y - a.y * b.x; }
  30. bool On(const Point &p, const Point &a, const Point &b) { return !dcmp(Cross(a - p, b - p)) && dcmp(Dot(a - p, b - p)) <= ; }
  31. Vector Normal(const Vector &a) { int l = Len(a); return Vector(-a.y / l, a.x / l); }
  32.  
  33. struct Seg {
  34. Point a, b;
  35. Seg() {}
  36. Seg(const Point &a, const Point &b): a(a), b(b) {}
  37. } queue[ + ];
  38.  
  39. struct Polygon {
  40. std::vector<Point> poly;
  41. int n;
  42. bool In(const Point &p) {
  43. int wn = ;
  44. for (int i = ; i <= n; i++) {
  45. if (On(p, poly[i], poly[i % n + ])) return ;
  46. int k = dcmp(Cross(poly[i % n + ] - poly[i], p - poly[i]));
  47. int d1 = dcmp(poly[i].y - p.y);
  48. int d2 = dcmp(poly[i % n + ].y - p.y);
  49. if (k > && d1 <= && d2 > ) wn++;
  50. if (k < && d2 <= && d1 > ) wn--;
  51. }
  52. if (wn) return ;
  53. return ;
  54. }
  55. } island[N];
  56.  
  57. struct near {
  58. Point p;
  59. double dis;
  60. near() {}
  61. near(const Point &a, const double &b): p(a), dis(b) {}
  62. };
  63.  
  64. int n, m;
  65.  
  66. void init() {
  67. scanf("%d%d", &n, &m);
  68. for (int i = ; i <= m; i++)
  69. flight[i].read();
  70. for (int i = ; i <= n; i++) {
  71. scanf("%d", &island[i].n);
  72. island[i].poly.resize(island[i].n + );
  73. for (int j = ; j <= island[i].n; j++)
  74. island[i].poly[j].read();
  75. }
  76. }
  77.  
  78. bool check(const Point &p) {
  79. for (int i = ; i <= n; i++)
  80. if (island[i].In(p)) return ;
  81. return ;
  82. }
  83.  
  84. Point GetLineIntersection(const Point &P, const Vector &v, const Point &Q, const Vector &w) {
  85. Vector u = P - Q;
  86. double t = Cross(w, u) / Cross(v, w);
  87. return P + v * t;
  88. }
  89. // 点 a 到线段 bc 的最近点
  90. near DISPS(const Point &a, const Point &b, const Point &c) {
  91. if (b == c) return near(b, Len(b - a));
  92. Vector v1 = c - b, v2 = a - b, v3 = a - c;
  93. if (dcmp(Dot(v1, v2)) <= ) return near(b, Len(v2));
  94. if (dcmp(Dot(v1, v3)) >= ) return near(c, Len(v3));
  95. Vector v = Normal(b - c);
  96. Point p = GetLineIntersection(a, v, b, v1);
  97. return near(p, Len(a - p));
  98. }
  99.  
  100. double ans;
  101.  
  102. near Find(const Point &p) {
  103. if (check(p)) return near(p, );
  104. near ans1;
  105. ans1.dis = 1e10;
  106. for (int i = ; i <= n; i++)
  107. for (int j = ; j <= island[i].n; j++) {
  108. near cur = DISPS(p, island[i].poly[j], island[i].poly[j % island[i].n + ]);
  109. if (dcmp(ans1.dis - cur.dis) >= ) ans1 = cur;
  110. }
  111. ans = std::max(ans, ans1.dis);
  112. return ans1;
  113. }
  114.  
  115. const int M = 1e5;
  116.  
  117. void solve() {
  118. int head = , tail = ;
  119. for (int i = ; i < m; i++)
  120. queue[++tail] = Seg(flight[i], flight[i + ]), Find(flight[i]);
  121. Find(flight[m]);
  122. while (head != tail) {
  123. Seg cur = queue[head = head % M + ];
  124. Point p1 = Find(cur.a).p, p2 = Find(cur.b).p, l = cur.a, r = cur.b;
  125. while (Len(r - l) > 1e-) {
  126. Point mid = (l + r) / ;
  127. if (Len(mid - p1) < Len(mid - p2)) l = mid;
  128. else r = mid;
  129. }
  130. double nowans = std::min(Len(l - p1), Len(l - p2));
  131. Find(l);
  132. if (ans + 0.0005 < nowans) {
  133. queue[tail = tail % M + ] = Seg(cur.a, l);
  134. queue[tail = tail % M + ] = Seg(l, cur.b);
  135. }
  136. }
  137. }
  138.  
  139. int main() {
  140. freopen("in.txt", "r", stdin);
  141. init();
  142. solve();
  143. printf("%.2f\n", ans);
  144. return ;
  145. }

1021. [SHOI2008]Debt 循环的债务

自己的DP真是弱爆了...虽然其他也弱...看完别人的题解恍然大悟...

$dp[i][j][k]$ 表示前 $i$ 种钞票,第一个人有 $j$ 块钱,第二个人有 $k$ 块钱所需要交换的次数。

$dp[0][第一个人初始钱数][第二个人初始钱数] = 0$

然后就暴力枚举两个人要多少张 $i$ 钞票转移即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. const int INF = 0x3f3f3f3f;
  5. const int val[] = {, , , , , , };
  6. int dp[][N][N], sum, cnt[][], d[], e[];
  7.  
  8. inline bool chkmin(int &a, const int &b) {
  9. return a > b ? a = b, : ;
  10. }
  11.  
  12. int main() {
  13. int a, b, c;
  14. scanf("%d%d%d", &a, &b, &c);
  15. for (int i = ; i <= ; i++)
  16. for (int j = ; j <= ; j++) {
  17. scanf("%d", &cnt[i][j]);
  18. sum += cnt[i][j] * val[j];
  19. d[i] += cnt[i][j] * val[j];
  20. e[j] += cnt[i][j];
  21. }
  22. int lasta = d[] - a + c, lastb = d[] - b + a, lastc = sum - lasta - lastb;
  23. if (lasta < || lastb < || lastc < ) {
  24. puts("impossible");
  25. return ;
  26. }
  27. memset(dp, 0x3f, sizeof dp);
  28. dp[][d[]][d[]] = ;
  29. for (int i = ; i <= ; i++)
  30. for (int j = ; j <= sum; j++)
  31. for (int k = ; k + j <= sum; k++) if (dp[i - ][j][k] != INF) {
  32. chkmin(dp[i][j][k], dp[i - ][j][k]);
  33. for (int m = ; m <= e[i]; m++)
  34. for (int n = ; n + m <= e[i]; n++) {
  35. int difa = m - cnt[][i], difb = n - cnt[][i];
  36. int jj = j + difa * val[i], kk = k + difb * val[i];
  37. if (jj < || kk < || sum - jj - kk < ) continue;
  38. chkmin(dp[i][jj][kk], dp[i - ][j][k] + (std::abs(difa) + std::abs(difb) + std::abs(difa + difb)) / );
  39. }
  40. }
  41. if (dp[][lasta][lastb] == INF) puts("impossible");
  42. else printf("%d\n", dp[][lasta][lastb]);
  43. return ;
  44. }

1022. [SHOI2008]小约翰的游戏John

Anti-Nim模板题。

先手必胜当且仅当

1. 所有石堆个数小于 $2$ 且 SG 值为 $0$

2. 存在一个石堆个数不小于 $2$ 且 SG 值不为 $0$

  1. #include <bits/stdc++.h>
  2.  
  3. int main() {
  4. int T;
  5. scanf("%d", &T);
  6. while (T--) {
  7. int n;
  8. scanf("%d", &n);
  9. bool flag = ;
  10. int sg = ;
  11. while (n--) {
  12. int x;
  13. scanf("%d", &x);
  14. sg ^= x;
  15. if (x > ) flag = ;
  16. }
  17. if (flag)
  18. puts(sg ? "John" : "Brother");
  19. else
  20. puts(!sg ? "John" : "Brother");
  21. }
  22. return ;
  23. }

1023. [SHOI2008]cactus仙人掌图

考虑求树上的直径,$f[u]$ 表示 $u$ 到以 $u$ 为子树中的节点的距离最大值,$ans$ 表示树的直径。

$ans = max \{f[u] + f[v] + 1 \}, v \in son(u)$

$f[u] = max \{f[v] + 1 \}, v \in son(u)$

如果能将环缩成点,那么就直接做就行了。

先跑tarjan,求出 $low$、$dfn$、$dep$ 数组,如果这条边是一条树边,即满足 $dfn[u] < low[v]$,$v \in son(u)$,就直接按上面的方法更新。

否则不更新。

然后如果 $dfn[u] < dfn[v] \wedge fa[v] \neq u$,则说明 $u$ 到 $v$ 是一个环,并且 $u$ 是环的起点,$v$ 是环的终点。

就把这个环单独处理一下,并把最长链的信息整合到 $u$ 上即可。

对一个环上处理,即把环上所有顶点拿出来暴力更新答案。

把环上节点按深度依次放到数组里,$C$ 代表这个环,$L$ 代表环的节点个数。

$ans = max \{ f[u] + f[v] + dis(u, v) \}, u \in C, v \in C$

$dis(u, v) = min \{ dep[v] - dep[u], L - (dep[v] - dep[u]) \}$

在数组里按深度排好序之后就可以用下标之差来表示距离了。

$ans = max \{ f[v] + v + f[u] - u \},2 \times dis(u, v) \leq L$

可以用单调队列优化。注意得把数组复制一下,因为最优点有可能被 $u$ 和 $v$ 隔开了。

$f[u] = max \{ f[v] + dis(u, v) \}$ 把最长链合并到 $u$ 上。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO
  4. {
  5. char buf[ << ], buf2[ << ], a[], *p1 = buf, *p2 = buf, hh = '\n';
  6. int p, p3 = -;
  7. void read() {}
  8. void print() {}
  9. inline int getc() {
  10. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, , << , stdin), p1 == p2) ? EOF : *p1++;
  11. }
  12. inline void flush() {
  13. fwrite(buf2, , p3 + , stdout), p3 = -;
  14. }
  15. template <typename T, typename... T2>
  16. inline void read(T &x, T2 &... oth) {
  17. T f = ; x = ;
  18. char ch = getc();
  19. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getc(); }
  20. while (isdigit(ch)) { x = x * + ch - ; ch = getc(); }
  21. x *= f;
  22. read(oth...);
  23. }
  24. template <typename T, typename... T2>
  25. inline void print(T x, T2... oth) {
  26. if (p3 > << ) flush();
  27. if (x < ) buf2[++p3] = , x = -x;
  28. do {
  29. a[++p] = x % + ;
  30. } while (x /= );
  31. do {
  32. buf2[++p3] = a[p];
  33. } while (--p);
  34. buf2[++p3] = hh;
  35. print(oth...);
  36. }
  37. }
  38. #define read IO::read
  39. #define print IO::print
  40.  
  41. inline bool chkmin(int &x, int y) { return x > y ? x = y, : ; }
  42. inline bool chkmax(int &x, int y) { return x < y ? x = y, : ; }
  43.  
  44. const int N = 1e5 + ;
  45. std::vector<int> vec[N];
  46. int fa[N], dfn[N], low[N], tol, dep[N], n, m, dp[N], ans;
  47. int que[N], a[N];
  48.  
  49. void solve(int u, int v) {
  50. int cnt = dep[v] - dep[u] + ;
  51. for (int i = v; i != u; i = fa[i])
  52. a[cnt--] = dp[i];
  53. a[] = dp[u];
  54. cnt = dep[v] - dep[u] + ;
  55. for (int i = ; i <= cnt; i++)
  56. a[i + cnt] = a[i];
  57. int l = , r = ;
  58. que[l = r = ] = ;
  59. for (int i = ; i <= cnt + (cnt >> ); i++) {
  60. if (i - que[l] > (cnt >> )) l++;
  61. chkmax(ans, a[i] + i + a[que[l]] - que[l]);
  62. while (l <= r && a[i] - i >= a[que[r]] - que[r]) r--;
  63. que[++r] = i;
  64. }
  65. for (int i = ; i <= cnt; i++)
  66. chkmax(dp[u], a[i] + std::min(i - , cnt - i + ));
  67. }
  68.  
  69. void dfs(int u, int pre) {
  70. fa[u] = pre; dep[u] = dep[pre] + ;
  71. dfn[u] = low[u] = ++tol;
  72. for (int v: vec[u]) {
  73. if (v == pre) continue;
  74. if (!dfn[v]) {
  75. dfs(v, u);
  76. chkmin(low[u], low[v]);
  77. } else {
  78. chkmin(low[u], dfn[v]);
  79. }
  80. if (dfn[u] < low[v]) chkmax(ans, dp[u] + dp[v] + ), chkmax(dp[u], dp[v] + );
  81. }
  82. for (int v: vec[u]) {
  83. if (dfn[u] < dfn[v] && fa[v] != u)
  84. solve(u, v);
  85. }
  86. }
  87.  
  88. int main() {
  89. read(n, m);
  90. for (int i = ; i <= m; i++) {
  91. int k, u;
  92. read(k, u);
  93. for (int j = ; j <= k; j++) {
  94. int v;
  95. read(v);
  96. vec[u].push_back(v);
  97. vec[v].push_back(u);
  98. u = v;
  99. }
  100. }
  101. dfs(, );
  102. print(ans);
  103. IO::flush();
  104. return ;
  105. }

1024. [SCOI2009]生日快乐

由于 $n$ 很小,那么可以暴力搜一波。

因为所有面积必须相同,所以考虑每一刀的时候,左右两部分必须长度符合比例。

  1. #include <bits/stdc++.h>
  2.  
  3. double solve(double x, double y, int n) {
  4. if (x < y) std::swap(x, y);
  5. if (n == ) return x / y;
  6. double ans = 1e9;
  7. for (int i = ; i <= n / ; i++) {
  8. ans = std::min(ans, std::max(solve(x / n * i, y, i), solve(x / n * (n - i), y, n - i)));
  9. ans = std::min(ans, std::max(solve(x, y / n * i, i), solve(x, y / n * (n - i), n - i)));
  10. }
  11. return ans;
  12. }
  13.  
  14. int main() {
  15. double x, y;
  16. int n;
  17. scanf("%lf%lf%d", &x, &y, &n);
  18. printf("%.6f\n", solve(x, y, n));
  19. return ;
  20. }

1026. [SCOI2009]windy数

数位DP裸题啦。

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4.  
  5. int dp[][];
  6. int a[];
  7.  
  8. int DP(int pos, int pre, bool limit, bool lead) {
  9. if (pos < ) return ;
  10. if (!limit && !lead && dp[pos][pre] != -) return dp[pos][pre];
  11. int ans = ;
  12. int up = limit ? a[pos] : ;
  13. if (lead) {
  14. ans += DP(pos - , , limit && == up, );
  15. for (int i = ; i <= up; i++)
  16. ans += DP(pos - , i, limit && i == up, );
  17. } else {
  18. for (int i = ; i <= pre - && i <= up; i++)
  19. ans += DP(pos - , i, limit && i == up, );
  20. for (int i = pre + ; i <= up; i++)
  21. ans += DP(pos - , i, limit && i == up, );
  22. }
  23. if (!limit && !lead) dp[pos][pre] = ans;
  24. return ans;
  25. }
  26.  
  27. int solve(int x) {
  28. //if (x == 0) return 1;
  29. int pos = ;
  30. while (x) {
  31. a[pos++] = x % ;
  32. x /= ;
  33. }
  34. return DP(pos - , , , );
  35. }
  36.  
  37. signed main() {
  38. int l, r;
  39. scanf("%lld%lld", &l, &r);
  40. memset(dp, -, sizeof(dp));
  41. printf("%lld\n", solve(r) - solve(l - ));
  42. return ;
  43. }

1027. [JSOI2007]合金

计算几何的题怎么都这么妙啊!

首先第三个元素是没有用的,因为如果前两个符合了,第三个肯定也符合。

然后把第一个元素看成 $x$ 轴,第二个元素看成 $y$ 轴。

那么对于一些原材料能形成的合金肯定在这些原材料的凸包内。

枚举每一对原材料 $(i, j)$,若所有合金都在其逆时针方向,那么 $i$ 就向 $j$ 连一条边。

之后就是求这个图的最小环,floyd即可。

不过需要特判一种情况,当所有合金都是一个点时,且有原材料刚好就是这种合金,答案就是 $1$。

看到有的博客说,所有金属都在一条线段上要特判,其实这种情况能被后面处理掉。即 $i$ 会向 $j$ 连一条边,$j$ 会向 $i$ 连一条边,答案就是 $2$。

所以就不用特判啦。

  1. #include <bits/stdc++.h>
  2. #define db double
  3.  
  4. const db eps = 1e-;
  5. inline int sign(db k) { return k < -eps ? - : k > eps; }
  6. inline int cmp(db k1, db k2) { return sign(k1 - k2); }
  7.  
  8. struct P {
  9. db x, y;
  10. P() {}
  11. P(db x, db y): x(x), y(y) {}
  12. P operator + (const P &rhs) const { return P(x + rhs.x, y + rhs.y); }
  13. P operator - (const P &rhs) const { return P(x - rhs.x, y - rhs.y); }
  14. P operator * (const db &k) const { return P(x * k, y * k); }
  15. P operator / (const db &k) const { return P(x / k, y / k); }
  16. bool operator < (const P &rhs) const { int c = cmp(x, rhs.x); return c ? c == - : cmp(y, rhs.y) == -; }
  17. bool operator == (const P &rhs) const { return !cmp(x, rhs.x) && !cmp(y, rhs.y); }
  18. db distTo(const P &rhs) const { return (*this - rhs).abs(); }
  19. db alpha() { return atan2(y, x); }
  20. void read() { scanf("%lf%lf", &x, &y); }
  21. void print() { printf("%.10f %.10f\n", x, y); }
  22. db abs() { return sqrt(abs2()); }
  23. db abs2() { return x * x + y * y; }
  24. P rotate(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); }
  25. P rotate90() { return P(-y, x); }
  26. P unit() { return *this/abs(); }
  27. P normal() { return rotate90() / abs(); }
  28. int quad() { return sign(y) == || (sign(y) == && sign(x) >= ); }
  29. db dot(const P &p) const { return x * p.x + y * p.y; }
  30. db det(const P &p) const { return x * p.y - y * p.x; }
  31. };
  32.  
  33. #define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y))
  34. #define crossOp(p1, p2, p3) sign(cross(p1, p2, p3))
  35.  
  36. // 判断 p1p2 和 q1q2 是否相交
  37. bool chkLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  38. db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  39. return sign(a1 + a2) != ;
  40. }
  41. P isLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  42. assert(chkLL(p1, p2, q1, q2));
  43. db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  44. return (p1 * a2 + p2 * a1) / (a1 + a2);
  45. }
  46. bool intersect(db l1, db r1, db l2, db r2) {
  47. if (l1 > r1) std::swap(l1, r2); if (l2 > r2) std::swap(l2, r2);
  48. return !(cmp(r1, l2) == - || cmp(r2, l1) == -);
  49. }
  50. bool isSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  51. return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y)
  52. && crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <=
  53. && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) <= ;
  54. }
  55. bool isSS_strict(const P &p1, const P &p2, const P &q1, const P &q2) {
  56. return crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <
  57. && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) < ;
  58. }
  59. bool isMiddle(db a, db m, db b) {
  60. return sign(a - m) == || sign(b - m) == || (a < m != b < m);
  61. }
  62. bool isMiddle(const P &a, const P &m, const P &b) {
  63. return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
  64. }
  65. bool onSeg(const P &p1, const P &p2, const P &q) {
  66. return crossOp(p1, p2, q) == && isMiddle(p1, q, p2);
  67. }
  68. bool onSeg_strict(const P &p1, const P &p2, const P &q) {
  69. return crossOp(p1, p2, q) == && sign((q - p1).dot(p1 - p2)) * sign((q - p2).dot(p1 - p2)) < ;
  70. }
  71. P proj(const P &p1, const P &p2, const P &q) {
  72. P dir = p2 - p1;
  73. return p1 + dir * (dir.dot(q - p1) / dir.abs2());
  74. }
  75. P reflect(const P &p1, const P &p2, const P &q) {
  76. return proj(p1, p2, q) * - q;
  77. }
  78. db nearest(const P &p1, const P &p2, const P &q) {
  79. P h = proj(p1, p2, q);
  80. if (isMiddle(p1, h, p2)) return q.distTo(h);
  81. return std::min(p1.distTo(q), p2.distTo(q));
  82. }
  83. db disSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  84. if (isSS(p1, p2, q1, q2)) return ;
  85. return std::min(std::min(nearest(p1, p2, q1), nearest(p1, p2, q2)), std::min(nearest(q1, q2, p1), nearest(q1, q2, p2)));
  86. }
  87. db rad(const P &p1, const P &p2) {
  88. return atan2l(p1.det(p2), p1.dot(p2));
  89. }
  90.  
  91. const int N = ;
  92. int n, m;
  93. P material[N], alloy[N];
  94.  
  95. bool spj() {
  96. for (int i = ; i < n; i++)
  97. if (!(alloy[i] == alloy[i - ]))
  98. return ;
  99. for (int i = ; i < m; i++)
  100. if (alloy[] == material[i])
  101. return ;
  102. return ;
  103. }
  104.  
  105. const int INF = 0x3f3f3f3f;
  106. int dis[N][N];
  107.  
  108. inline bool chkmin(int &a, int b) { return a > b ? a = b, : ; }
  109.  
  110. int floyd() {
  111. for (int k = ; k < m; k++)
  112. for (int i = ; i < m; i++)
  113. if (dis[i][k] < INF)
  114. for (int j = ; j < m; j++)
  115. chkmin(dis[i][j], dis[i][k] + dis[k][j]);
  116. int ans = INF;
  117. for (int i = ; i < m; i++)
  118. chkmin(ans, dis[i][i]);
  119. return ans;
  120. }
  121.  
  122. void addedge(int p, int q) {
  123. for (int i = ; i < n; i++) {
  124. int k = crossOp(material[p], material[q], alloy[i]);
  125. if (k > ) continue;
  126. if (k < ) return;
  127. if (!onSeg(material[p], material[q], alloy[i])) return;
  128. }
  129. dis[p][q] = ;
  130. }
  131.  
  132. int main() {
  133. scanf("%d%d", &m, &n);
  134. for (int i = ; i < m; i++) {
  135. material[i].read();
  136. db x;
  137. scanf("%lf", &x);
  138. }
  139. for (int i = ; i < n; i++) {
  140. alloy[i].read();
  141. db x;
  142. scanf("%lf", &x);
  143. }
  144. if (spj()) {
  145. puts("");
  146. return ;
  147. }
  148. memset(dis, 0x3f, sizeof dis);
  149. for (int i = ; i < m; i++)
  150. for (int j = ; j < m; j++)
  151. if (i != j)
  152. addedge(i, j);
  153. int ans = floyd();
  154. printf("%d\n", ans == INF ? - : ans);
  155. return ;
  156. }

1028. [JSOI2007]麻将

暴力枚举要补哪张牌,再枚举谁是对子,最后 check 一下剩下能否组成顺子或刻子,优先 check 刻子。

  1. #include <cstdio>
  2. #include <vector>
  3. #include <algorithm>
  4.  
  5. const int N = ;
  6. int a[N], cnt[N], n, m, temp[N];
  7.  
  8. bool solve() {
  9. for (int i = ; i <= n; i++) {
  10. if (cnt[i] < ) continue;
  11. bool flag = ;
  12. cnt[i] -= ;
  13. for (int j = ; j <= n + ; j++)
  14. temp[j] = cnt[j];
  15. for (int j = ; j <= n + ; j++) {
  16. if (temp[j] < ) { flag = ; break; }
  17. temp[j] %= ;
  18. temp[j + ] -= temp[j];
  19. temp[j + ] -= temp[j];
  20. }
  21. cnt[i] += ;
  22. if (flag) return ;
  23. }
  24. return ;
  25. }
  26.  
  27. int main() {
  28. scanf("%d%d", &n, &m);
  29. for (int i = ; i <= * m + ; i++) {
  30. int x;
  31. scanf("%d", &x);
  32. cnt[x]++;
  33. }
  34. std::vector<int> ans;
  35. for (int i = ; i <= n; i++) {
  36. cnt[i]++;
  37. if (solve())
  38. ans.push_back(i);
  39. cnt[i]--;
  40. }
  41. if (ans.size() == ) puts("NO");
  42. else for (int x: ans)
  43. printf("%d ", x);
  44. puts("");
  45. return ;
  46. }

1029. [JSOI2007]建筑抢修

ddl 靠前的先做,如果做不了,看看之前有没有耗时比它多的,有的话换掉,用堆维护。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = 2e5 + ;
  5. struct P {
  6. ll x, y;
  7. bool operator < (const P &pp) const { return y < pp.y; }
  8. } p[N];
  9.  
  10. int main() {
  11. int n;
  12. scanf("%d", &n);
  13. for (int i = ; i <= n; i++)
  14. scanf("%lld%lld", &p[i].x, &p[i].y);
  15. ll cur = ;
  16. std::sort(p + , p + + n);
  17. std::priority_queue<int> que;
  18. que.push();
  19. int ans = ;
  20. for (int i = ; i <= n; i++) {
  21. if (cur + p[i].x <= p[i].y) {
  22. cur += p[i].x;
  23. que.push(p[i].x);
  24. ans++;
  25. } else if (p[i].x < que.top()) {
  26. cur += p[i].x - que.top();
  27. que.pop();
  28. que.push(p[i].x);
  29. }
  30. }
  31. printf("%d\n", ans);
  32. return ;
  33. }

1030. [JSOI2007]文本生成器

建出AC自动机后DP,注意第一维要枚举长度,因为第一维枚举节点的话一些节点会往回跳。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. const int sz = ;
  5. const int MOD = ;
  6.  
  7. void M(int &a) { if (a >= MOD) a -= MOD; }
  8.  
  9. int n, m;
  10.  
  11. struct Aho {
  12. int trie[N][sz], tol, fail[N], last[N];
  13. bool flag[N];
  14. int dp[N][];
  15. void init() {
  16. tol = ;
  17. memset(dp, , sizeof(dp));
  18. newnode();
  19. }
  20. int newnode() {
  21. for (int i = ; i < sz; i++)
  22. trie[tol][i] = ;
  23. fail[tol] = flag[tol] = last[tol] = ;
  24. return tol++;
  25. }
  26. void insert(char *s) {
  27. int n = strlen(s), cur = ;;
  28. for (int i = ; i < n; i++) {
  29. int id = s[i] - 'A';
  30. if (!trie[cur][id]) trie[cur][id] = newnode();
  31. cur = trie[cur][id];
  32. }
  33. flag[cur] = ;
  34. }
  35. int build() {
  36. std::queue<int> que;
  37. for (int i = ; i < sz; i++)
  38. if (trie[][i]) que.push(trie[][i]);
  39. while (!que.empty()) {
  40. int u = que.front(); que.pop();
  41. flag[u] |= flag[fail[u]];
  42. for (int i = ; i < sz; i++) {
  43. int &v = trie[u][i];
  44. if (v) {
  45. fail[v] = trie[fail[u]][i];
  46. que.push(v);
  47. last[v] = flag[fail[v]] ? fail[v] : last[fail[v]];
  48. } else {
  49. v = trie[fail[u]][i];
  50. }
  51. }
  52. }
  53. dp[][] = ;
  54. for (int j = ; j < m; j++)
  55. for (int i = ; i < tol; i++) if (dp[i][j] && !flag[i])
  56. for (int k = ; k < sz; k++) {
  57. M(dp[trie[i][k]][j + ] += dp[i][j]);
  58. }
  59. int ans = ;
  60. for (int i = ; i < tol; i++)
  61. if (!flag[i])
  62. M(ans += dp[i][m]);
  63. return ans;
  64. }
  65. } ac;
  66.  
  67. char s[N];
  68.  
  69. int qp(int a, int b) {
  70. int ans = ;
  71. while (b) {
  72. if (b & ) ans = ans * a % MOD;
  73. a = a * a % MOD;
  74. b >>= ;
  75. }
  76. return ans;
  77. }
  78.  
  79. int main() {
  80. while (~scanf("%d%d", &n, &m)) {
  81. ac.init();
  82. for (int i = ; i < n; i++)
  83. scanf("%s", s), ac.insert(s);
  84. printf("%d\n", (qp(, m) - ac.build() + MOD) % MOD);
  85. }
  86. return ;
  87. }

1031. [JSOI2007]字符加密Cipher

把字符串复制一下,求个后缀数组,再按sa枚举后缀,判断长度是否大于等于原字符串长度,注意坑就是第 $1$ 和 第 $len + 1$ 个后缀可能会重复。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 2e5 + ;
  4. char s[N];
  5.  
  6. namespace SA {
  7. int sa[N], rk[N], fir[N], sec[N], c[N], height[N];
  8. void build(int len, int num = ) {
  9. register int i, j, k;
  10. for (i = ; i <= num; i++) c[i] = ;
  11. for (i = ; i <= len; i++) ++c[fir[i] = s[i]];
  12. for (i = ; i <= num; i++) c[i] += c[i - ];
  13. for (i = len; i >= ; i--) sa[c[fir[i]]--] = i;
  14. for (k = ; k <= len; k <<= ) {
  15. int cnt = ;
  16. for (i = len - k + ; i <= len; i++) sec[++cnt] = i;
  17. for (i = ; i <= len; i++) if (sa[i] > k) sec[++cnt] = sa[i] - k;
  18. for (i = ; i <= num; i++) c[i] = ;
  19. for (i = ; i <= len; i++) ++c[fir[i]];
  20. for (i = ; i <= num; i++) c[i] += c[i - ];
  21. for (i = len; i >= ; i--) sa[c[fir[sec[i]]]--] = sec[i], sec[i] = ;
  22. std::swap(fir, sec);
  23. fir[sa[]] = ; cnt = ;
  24. for (i = ; i <= len; i++)
  25. fir[sa[i]] = (sec[sa[i]] == sec[sa[i - ]] && sec[sa[i] + k] == sec[sa[i - ] + k]) ? cnt : ++cnt;
  26. if (cnt == len) break;
  27. num = cnt;
  28. }
  29. k = ;
  30. for (i = ; i <= len; i++) rk[sa[i]] = i;
  31. for (i = ; i <= len; i++) {
  32. if (rk[i] == ) continue;
  33. if (k) k--;
  34. j = sa[rk[i] - ];
  35. while (j + k <= len && i + k <= len && s[i + k] == s[j + k]) k++;
  36. height[rk[i]] = k;
  37. }
  38. }
  39. } using namespace SA;
  40.  
  41. char ans[N];
  42. int cnt;
  43. bool vis[N];
  44.  
  45. int id(int x, int len) {
  46. if (x > len) x -= len;
  47. return x;
  48. }
  49.  
  50. int main() {
  51. scanf("%s", s + );
  52. int len = strlen(s + );
  53. for (int i = ; i <= len; i++)
  54. s[i + len] = s[i];
  55. build(len * );
  56. for (int i = ; i <= * len; i++) {
  57. if (sa[i] <= len + && !vis[id(sa[i], len)]) {
  58. ans[++cnt] = s[sa[i] + len - ];
  59. vis[id(sa[i], len)] = ;
  60. if (cnt == len) {
  61. puts(ans + );
  62. return ;
  63. }
  64. }
  65. }
  66. return ;
  67. }

1032. [JSOI2007]祖码Zuma

区间DP,把同颜色的段缩成一个点,用 $num$ 数组表示个数。

$f[l][r]$ 表示消除 $l$ 到 $r$ 区间所有点的最小花费。

当 $num[i] > 1$,$f[i][i] = 1$,否则等于 $2$。

然后区间DP,若左右端点相同时 $f[l][r] = f[l + 1][r - 1] + c$,其中当 $num[l] + num[r] > 2$ 时,$c = 0$,否则 $c = 1$。

然后枚举中断点即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int INF = 0x3f3f3f3f;
  4. const int N = ;
  5. int f[N][N], num[N], color[N], cnt, n;
  6. int a[N];
  7.  
  8. int main() {
  9. memset(f, 0x3f, sizeof f);
  10. scanf("%d", &n);
  11. for (int i = ; i <= n; i++)
  12. scanf("%d", a + i);
  13. int cur = ;
  14. for (int i = ; i <= n; i++) {
  15. if (a[i] != a[i - ]) {
  16. color[++cnt] = a[i - ];
  17. num[cnt] = cur;
  18. cur = ;
  19. } else {
  20. cur++;
  21. }
  22. }
  23. color[++cnt] = a[n];
  24. num[cnt] = cur;
  25. for (int i = ; i <= cnt; i++)
  26. f[i][i] = num[i] > ? : ;
  27. for (int len = ; len <= cnt; len++)
  28. for (int l = ; l <= cnt; l++) {
  29. int r = l + len - ;
  30. if (r > cnt) break;
  31. if (color[l] == color[r])
  32. f[l][r] = f[l + ][r - ] + ((num[l] + num[r]) > ? : );
  33. for (int k = l; k < r; k++)
  34. f[l][r] = std::min(f[l][r], f[l][k] + f[k + ][r]);
  35. }
  36. printf("%d\n", f[][cnt]);
  37. return ;
  38. }

1034. [ZJOI2008]泡泡堂BNB

感觉我双指针不行啊...写了个multiset过的。看了别人代码改了发双指针。

写几个样例大概就知道跟田忌赛马一样了。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e5 + ;
  4. int a[N], b[N];
  5. std::multiset<int> st1, st2;
  6. bool vis[N];
  7.  
  8. int solve(int A[], int B[], int n) {
  9. int ans = ;
  10. int l1 = , l2 = , r1 = n, r2 = n;
  11. while (l1 <= r1 && l2 <= r2) {
  12. if (A[l1] > B[l2]) ans += , l1++, l2++;
  13. else if (A[r1] > B[r2]) ans += , r1--, r2--;
  14. else ans += (A[l1] == B[r2]) ? : , l1++, r2--;
  15. }
  16. return ans;
  17. }
  18.  
  19. int main() {
  20. int n;
  21. scanf("%d", &n);
  22. for (int i = ; i <= n; i++)
  23. scanf("%d", a + i);
  24. for (int i = ; i <= n; i++)
  25. scanf("%d", b + i);
  26. std::sort(a + , a + + n);
  27. std::sort(b + , b + + n);
  28. printf("%d %d\n", solve(a, b, n), * n - solve(b, a, n));
  29. }

1036. [ZJOI2008]树的统计Count

树剖板子题。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. template<typename T>
  5. inline void read(T &x) {
  6. x = ; T f = ; char ch = getchar();
  7. while (ch < '' || ch > '') { if (ch == '-') f = -; ch = getchar(); }
  8. while (ch >= '' && ch <= '') { x = x * + ch - ; ch = getchar(); }
  9. x *= f;
  10. }
  11.  
  12. const int INF = 0x3f3f3f3f;
  13. const int N = ;
  14. int w[N], wt[N], n, q;
  15. int sz[N], son[N], fa[N], dep[N];
  16. int dfn[N], tol, top[N];
  17. struct E { int v, ne; } e[N * ];
  18. int head[N], cnt;
  19.  
  20. struct Seg {
  21. #define lp p << 1
  22. #define rp p << 1 | 1
  23. int sum[N << ], mx[N << ];
  24. inline void pushup(int p) {
  25. sum[p] = sum[lp] + sum[rp];
  26. mx[p] = max(mx[lp], mx[rp]);
  27. }
  28. void build(int p, int l, int r) {
  29. if (l == r) {
  30. sum[p] = mx[p] = wt[l];
  31. return;
  32. }
  33. int mid = l + r >> ;
  34. build(lp, l, mid);
  35. build(rp, mid + , r);
  36. pushup(p);
  37. }
  38. void update(int p, int l, int r, int pos, int val) {
  39. if (l == r) {
  40. sum[p] = mx[p] = val;
  41. return;
  42. }
  43. int mid = l + r >> ;
  44. if (pos <= mid) update(lp, l, mid, pos, val);
  45. else update(rp, mid + , r, pos, val);
  46. pushup(p);
  47. }
  48. int query1(int p, int l, int r, int x, int y) {
  49. if (x <= l && y >= r) return sum[p];
  50. int ans = ;
  51. int mid = l + r >> ;
  52. if (x <= mid) ans += query1(lp, l, mid, x, y);
  53. if (y > mid) ans += query1(rp, mid + , r, x, y);
  54. return ans;
  55. }
  56. int query2(int p, int l, int r, int x, int y) {
  57. if (x <= l && y >= r) return mx[p];
  58. int ans = -INF;
  59. int mid = l + r >> ;
  60. if (x <= mid) ans = max(ans, query2(lp, l, mid, x, y));
  61. if (y > mid) ans = max(ans, query2(rp, mid + , r, x, y));
  62. return ans;
  63. }
  64. } seg;
  65.  
  66. inline void add(int u, int v) {
  67. e[++cnt].v = v; e[cnt].ne = head[u]; head[u] = cnt;
  68. e[++cnt].v = u; e[cnt].ne = head[v]; head[v] = cnt;
  69. }
  70.  
  71. void dfs1(int u, int pre, int d) {
  72. dep[u] = d;
  73. fa[u] = pre;
  74. sz[u] = ;
  75. for (int i = head[u]; i; i = e[i].ne) {
  76. int v = e[i].v;
  77. if (v == pre) continue;
  78. dfs1(v, u, d + );
  79. sz[u] += sz[v];
  80. if (sz[v] > sz[son[u]]) son[u] = v;
  81. }
  82. }
  83.  
  84. void dfs2(int u, int tp) {
  85. top[u] = tp;
  86. dfn[u] = ++tol;
  87. wt[tol] = w[u];
  88. if (!son[u]) return;
  89. dfs2(son[u], tp);
  90. for (int i = head[u]; i; i = e[i].ne) {
  91. int v = e[i].v;
  92. if (v != fa[u] && v != son[u])
  93. dfs2(v, v);
  94. }
  95. }
  96.  
  97. int solve1(int u, int v) {
  98. int ans = ;
  99. while (top[u] != top[v]) {
  100. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  101. ans += seg.query1(, , n, dfn[top[u]], dfn[u]);
  102. u = fa[top[u]];
  103. }
  104. if (dep[u] > dep[v]) swap(u, v);
  105. ans += seg.query1(, , n, dfn[u], dfn[v]);
  106. return ans;
  107. }
  108.  
  109. int solve2(int u, int v) {
  110. int ans = -INF;
  111. while (top[u] != top[v]) {
  112. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  113. ans = max(ans, seg.query2(, , n, dfn[top[u]], dfn[u]));
  114. u = fa[top[u]];
  115. }
  116. if (dep[u] > dep[v]) swap(u, v);
  117. ans = max(ans, seg.query2(, , n, dfn[u], dfn[v]));
  118. return ans;
  119. }
  120.  
  121. char s[];
  122.  
  123. int main() {
  124. read(n);
  125. for (int i = ; i < n; i++) {
  126. int u, v;
  127. read(u), read(v);
  128. add(u, v);
  129. }
  130. for (int i = ; i <= n; i++) read(w[i]);
  131. dfs1(, , );
  132. dfs2(, );
  133. seg.build(, , n);
  134. read(q);
  135. while (q--) {
  136. scanf("%s", s);
  137. int u, v;
  138. read(u), read(v);
  139. if (s[] == 'M') {
  140. printf("%d\n", solve2(u, v));
  141. } else if (s[] == 'S') {
  142. printf("%d\n", solve1(u, v));
  143. } else {
  144. seg.update(, , n, dfn[u], v);
  145. }
  146. }
  147. return ;
  148. }

1037. [ZJOI2008]生日聚会Party

$dp[i][j][k][l]$ 表示 $i$ 个男生,$j$ 个女生,结尾段男生最多比女生多 $k$ 个,女生最多比男生多 $l$ 个。

$dp[i + 1][j][k + 1][\max\{l - 1, 0\}]$ $+=$ $dp[i][j][k][l]$

$dp[i][j+1][\max\{k - 1, 0\}][l+1]$ $+=$ $dp[i][j][k][l]$

  1. #include <bits/stdc++.h>
  2.  
  3. const int MOD = ;
  4. const int N = ;
  5. int dp[N][N][][];
  6.  
  7. void M(int &x) {
  8. if (x >= MOD) x -= MOD;
  9. if (x < ) x += MOD;
  10. }
  11.  
  12. int main() {
  13. int n, m, k;
  14. scanf("%d%d%d", &n, &m, &k);
  15. dp[][][][] = ;
  16. for (int i = ; i <= n; i++)
  17. for (int j = ; j <= m; j++)
  18. for (int o = ; o <= k; o++)
  19. for (int p = ; p <= k; p++) if (dp[i][j][o][p]) {
  20. if (i < n && o < k)
  21. M(dp[i + ][j][o + ][std::max(p - , )] += dp[i][j][o][p]);
  22. if (j < m && p < k)
  23. M(dp[i][j + ][std::max(o - , )][p + ] += dp[i][j][o][p]);
  24. }
  25. int ans = ;
  26. for (int i = ; i <= k; i++)
  27. for (int j = ; j <= k; j++)
  28. M(ans += dp[n][m][i][j]);
  29. printf("%d\n", ans);
  30. return ;
  31. }

1038. [ZJOI2008]瞭望塔

能看到其他所有点的区域就是轮廓线的半平面交。
然后最小高度就是半平面交与轮廓线这两个一次分段函数的差,极值肯定出现在分段点上,分别求一下即可。

  1. #include <bits/stdc++.h>
  2.  
  3. #define db double
  4.  
  5. const db eps = 1e-;
  6. inline int sign(db k) { return k < -eps ? - : k > eps; }
  7. inline int cmp(db k1, db k2) { return sign(k1 - k2); }
  8.  
  9. struct P {
  10. db x, y;
  11. P() {}
  12. P(db x, db y): x(x), y(y) {}
  13. P operator + (const P &rhs) const { return P(x + rhs.x, y + rhs.y); }
  14. P operator - (const P &rhs) const { return P(x - rhs.x, y - rhs.y); }
  15. P operator * (const db &k) const { return P(x * k, y * k); }
  16. P operator / (const db &k) const { return P(x / k, y / k); }
  17. bool operator < (const P &rhs) const { int c = cmp(x, rhs.x); return c ? c == - : cmp(y, rhs.y) == -; }
  18. bool operator == (const P &rhs) const { return !cmp(x, rhs.x) && !cmp(y, rhs.y); }
  19. db distTo(const P &rhs) const { return (*this - rhs).abs(); }
  20. db alpha() { return atan2(y, x); }
  21. void read() { scanf("%lf%lf", &x, &y); }
  22. void print() { printf("%.10f %.10f\n", x, y); }
  23. db abs() { return sqrt(abs2()); }
  24. db abs2() { return x * x + y * y; }
  25. P rot(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); }
  26. P rot90() { return P(-y, x); }
  27. P unit() { return *this / abs(); }
  28. P normal() { return rot90() / abs(); }
  29. int quad() { return sign(y) == || (sign(y) == && sign(x) >= ); }
  30. db dot(const P &p) const { return x * p.x + y * p.y; }
  31. db det(const P &p) const { return x * p.y - y * p.x; }
  32. };
  33.  
  34. struct L { // ps[0] -> ps[1]
  35. P ps[];
  36. L() {}
  37. L(const P &p0, const P &p1) {
  38. ps[] = p0; ps[] = p1;
  39. }
  40. P &operator[](int i) { return ps[i]; }
  41. P dir() { return ps[] - ps[]; }
  42. bool include(const P &p) { return sign((ps[] - ps[]).det(p - ps[])) > ; }
  43. L push() { // push eps outawrd
  44. const db Eps = 1e-;
  45. P delta = (ps[] - ps[]).normal() * Eps;
  46. return {ps[] - delta, ps[] - delta};
  47. }
  48. };
  49.  
  50. #define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y))
  51. #define crossOp(p1, p2, p3) sign(cross(p1, p2, p3))
  52.  
  53. // 判断 p1p2 和 q1q2 是否相交
  54. bool chkLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  55. db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  56. return sign(a1 + a2) != ;
  57. }
  58. // 直线交点
  59. P isLL(const P &p1, const P &p2, const P &q1, const P &q2) {
  60. assert(chkLL(p1, p2, q1, q2));
  61. db a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2);
  62. return (p1 * a2 + p2 * a1) / (a1 + a2);
  63. }
  64. P isLL(L l1, L l2) {
  65. return isLL(l1[], l1[], l2[], l2[]);
  66. }
  67. /***** 线段相交 *****/
  68. bool intersect(db l1, db r1, db l2, db r2) {
  69. if (l1 > r1) std::swap(l1, r2); if (l2 > r2) std::swap(l2, r2);
  70. return !(cmp(r1, l2) == - || cmp(r2, l1) == -);
  71. }
  72. bool isSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  73. return intersect(p1.x, p2.x, q1.x, q2.x) && intersect(p1.y, p2.y, q1.y, q2.y)
  74. && crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <=
  75. && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) <= ;
  76. }
  77. bool isSS_strict(const P &p1, const P &p2, const P &q1, const P &q2) {
  78. return crossOp(p1, p2, q1) * crossOp(p1, p2, q2) <
  79. && crossOp(q1, q2, p1) * crossOp(q1, q2, p2) < ;
  80. }
  81. /***************/
  82. /***** 点在线段上判定 *****/
  83. bool isMiddle(db a, db m, db b) {
  84. return sign(a - m) == || sign(b - m) == || (a < m != b < m);
  85. }
  86. bool isMiddle(const P &a, const P &m, const P &b) {
  87. return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
  88. }
  89. bool onSeg(const P &p1, const P &p2, const P &q) {
  90. return crossOp(p1, p2, q) == && isMiddle(p1, q, p2);
  91. }
  92. bool onSeg_strict(const P &p1, const P &p2, const P &q) {
  93. return crossOp(p1, p2, q) == && sign((q - p1).dot(p1 - p2)) * sign((q - p2).dot(p1 - p2)) < ;
  94. }
  95. /*******************/
  96. // 投影
  97. P proj(const P &p1, const P &p2, const P &q) {
  98. P dir = p2 - p1;
  99. return p1 + dir * (dir.dot(q - p1) / dir.abs2());
  100. }
  101. // 反射
  102. P reflect(const P &p1, const P &p2, const P &q) {
  103. return proj(p1, p2, q) * - q;
  104. }
  105. // 最近点
  106. db nearest(const P &p1, const P &p2, const P &q) {
  107. P h = proj(p1, p2, q);
  108. if (isMiddle(p1, h, p2)) return q.distTo(h);
  109. return std::min(p1.distTo(q), p2.distTo(q));
  110. }
  111. // 线段距离
  112. db disSS(const P &p1, const P &p2, const P &q1, const P &q2) {
  113. if (isSS(p1, p2, q1, q2)) return ;
  114. return std::min(std::min(nearest(p1, p2, q1), nearest(p1, p2, q2)), std::min(nearest(q1, q2, p1), nearest(q1, q2, p2)));
  115. }
  116. // 夹角
  117. db rad(const P &p1, const P &p2) {
  118. return atan2l(p1.det(p2), p1.dot(p2));
  119. }
  120. // 多边形面积
  121. db area(const std::vector<P> &ps) {
  122. db ans = ;
  123. for (int i = , n = ps.size(); i < n; i++)
  124. ans += ps[i].det(ps[(i + ) % n]);
  125. return ans;
  126. }
  127. // 点包含 2: inside 1: onSeg 0: outside
  128. int contain(const std::vector<P> &ps, const P &p) {
  129. int n = ps.size(), ret = ;
  130. for (int i = ; i < n; i++) {
  131. P u = ps[i], v = ps[(i + ) % n];
  132. if (onSeg(u, v, p)) return ;
  133. if (cmp(u.y, v.y) <= ) std::swap(u, v);
  134. if (cmp(p.y, u.y) > || cmp(p.y, v.y) <= ) continue;
  135. ret ^= crossOp(p, u, v) > ;
  136. }
  137. return ret * ;
  138. }
  139. // 凸包
  140. std::vector<P> convexHull(std::vector<P> ps) {
  141. int n = ps.size(); if (n <= ) return ps;
  142. std::sort(ps.begin(), ps.end());
  143. std::vector<P> qs(n * ); int k = ;
  144. for (int i = ; i < n; qs[k++] = ps[i++])
  145. while (k > && crossOp(qs[k - ], qs[k - ], ps[i]) <= ) --k;
  146. for (int i = n - , t = k; i >= ; qs[k++] = ps[i--])
  147. while (k > t && crossOp(qs[k - ], qs[k - ], ps[i]) <= ) --k;
  148. qs.resize(k - );
  149. return qs;
  150. }
  151. std::vector<P> convexHullNonStrict(std::vector<P> ps) {
  152. int n = ps.size(); if (n <= ) return ps;
  153. std::sort(ps.begin(), ps.end());
  154. std::vector<P> qs(n * ); int k = ;
  155. for (int i = ; i < n; qs[k++] = ps[i++])
  156. while (k > && crossOp(qs[k - ], qs[k - ], ps[i]) < ) --k;
  157. for (int i = n - , t = k; i >= ; qs[k++] = ps[i--])
  158. while (k > t && crossOp(qs[k - ], qs[k - ], ps[i]) < ) --k;
  159. qs.resize(k - );
  160. return qs;
  161. }
  162. // 点集直径
  163. db convexDiameter(const std::vector<P> &ps) {
  164. int n = ps.size(); if (n <= ) return ;
  165. int is = , js = ;
  166. for (int k = ; k < n; k++)
  167. is = ps[k] < ps[is] ? k : is, js = ps[js] < ps[k] ? k : js;
  168. int i = is, j = js;
  169. db ret = ps[i].distTo(ps[j]);
  170. do {
  171. if ((ps[(i + ) % n] - ps[i]).det(ps[(j + ) % n] - ps[j]) >= )
  172. (++j) %= n;
  173. else
  174. (++i) %= n;
  175. ret = std::max(ret, ps[i].distTo(ps[j]));
  176. } while (i != is || j != js);
  177. return ret;
  178. }
  179. // convecCut
  180. std::vector<P> convexCut(const std::vector<P> &ps, const P &q1, const P &q2) {
  181. std::vector<P> qs;
  182. int n = ps.size();
  183. for (int i = ; i < n; i++) {
  184. P p1 = ps[i], p2 = ps[(i + ) % n];
  185. int d1 = crossOp(q1, q2, p1), d2 = crossOp(q1, q2, p2);
  186. if (d1 >= ) qs.push_back(p1);
  187. if (d1 * d2 < ) qs.push_back(isLL(p1, p2, q1, q2));
  188. }
  189. return qs;
  190. }
  191. // min_dis
  192. db min_dis(const std::vector<P> &ps, int l, int r) {
  193. if (r - l <= ) {
  194. db ret = 1e100;
  195. for (int i = l; i < r; i++)
  196. for (int j = l; j < i; j++)
  197. ret = std::min(ret, ps[i].distTo(ps[j]));
  198. return ret;
  199. }
  200. int mid = l + r >> ;
  201. db ret = std::min(min_dis(ps, l, mid), min_dis(ps, mid, r));
  202. std::vector<P> qs;
  203. for (int i = l; i < r; i++)
  204. if (cmp(fabs(ps[i].x - ps[mid].x), ret) <= ) qs.push_back(ps[i]);
  205. std::sort(qs.begin(), qs.end(), [](const P & a, const P & b) -> bool { return cmp(a.y, b.y) < ; });
  206. for (int i = ; i < qs.size(); i++)
  207. for (int j = i - ; j >= && cmp(qs[j].y, qs[i].y - ret) >= ; j--)
  208. ret = std::min(ret, qs[j].distTo(qs[i]));
  209. return ret;
  210. }
  211. // 圆的关系
  212. int type(const P &o1, db r1, const P &o2, db r2) {
  213. db d = o1.distTo(o2);
  214. if (cmp(d, r1 + r2) == ) return ; // 相离
  215. if (cmp(d, r1 + r2) == ) return ; // 外切
  216. if (cmp(d, fabs(r1 - r2)) == ) return ; // 相交
  217. if (cmp(d, fabs(r1 - r2)) == ) return ; // 内切
  218. return ;
  219. }
  220.  
  221. bool parallel(L l0, L l1) {
  222. return sign(l0.dir().det(l1.dir())) == ;
  223. }
  224.  
  225. bool sameDir(L l0, L l1) {
  226. return parallel(l0, l1) && sign(l0.dir().dot(l1.dir())) == ;
  227. }
  228.  
  229. bool cmp(P a, P b) {
  230. if (a.quad() != b.quad()) {
  231. return a.quad() < b.quad();
  232. } else {
  233. return sign(a.det(b)) > ;
  234. }
  235. }
  236.  
  237. bool operator < (L l0, L l1) {
  238. if (sameDir(l0, l1)) {
  239. return l1.include(l0[]);
  240. } else {
  241. return cmp(l0.dir(), l1.dir());
  242. }
  243. }
  244.  
  245. bool check(L u, L v, L w) {
  246. return w.include(isLL(u, v));
  247. }
  248.  
  249. const int N = 1e3 + ;
  250. L que[N];
  251.  
  252. std::vector<L> halfPlaneIS(std::vector<L> &l) {
  253. std::sort(l.begin(), l.end());
  254. int head = , tail = ;
  255. for (int i = ; i < l.size(); i++) {
  256. if (i && sameDir(l[i], l[i - ])) continue;
  257. while (tail - head > && !check(que[tail - ], que[tail - ], l[i])) tail--;
  258. while (tail - head > && !check(que[head + ], que[head], l[i])) head++;
  259. que[tail++] = l[i];
  260. }
  261. while (tail - head > && !check(que[tail - ], que[tail - ], que[])) tail--;
  262. while (tail - head > && !check(que[], que[], que[tail - ])) head++;
  263. std::vector<L> ans;
  264. for (int i = head; i < tail; i++)
  265. ans.push_back(que[i]);
  266. return ans;
  267. }
  268.  
  269. db gety(P p, std::vector<P> point, std::vector<L> line) {
  270. int n = point.size();
  271. if (sign(p.x - point[].x) <= ) return isLL(line[], L(p, P(p.x, p.y + ))).y;
  272. if (sign(p.x - point[n - ].x) >= ) return isLL(line[n], L(p, P(p.x, p.y + ))).y;
  273. for (int i = ; i < n - ; i++) {
  274. if (isMiddle(point[i].x, p.x, point[i + ].x))
  275. return isLL(line[i + ], L(p, P(p.x, p.y + ))).y;
  276. }
  277. return 1e11;
  278. }
  279.  
  280. db getyy(P p, std::vector<P> point) {
  281. for (int i = , sz = point.size(); i < sz - ; i++) {
  282. if (isMiddle(point[i].x, p.x, point[i + ].x))
  283. return isLL(point[i], point[i + ], p, P(p.x, p.y + )).y;
  284. }
  285. return 1e11;
  286. }
  287.  
  288. int main() {
  289. int n;
  290. scanf("%d", &n);
  291. if (n <= ) {
  292. puts("");
  293. return ;
  294. }
  295. std::vector<P> p(n);
  296. for (int i = ; i < n; i++)
  297. scanf("%lf", &p[i].x);
  298. for (int i = ; i < n; i++)
  299. scanf("%lf", &p[i].y);
  300. std::vector<L> l;
  301. for (int i = ; i < n - ; i++)
  302. l.push_back(L(p[i], p[i + ]));
  303. std::vector<L> half = halfPlaneIS(l);
  304. db ans = 1e10;
  305. std::vector<P> ss;
  306. for (int i = , sz = half.size(); i < sz - ; i++)
  307. ss.push_back(isLL(half[i], half[i + ]));
  308. for (int i = ; i < n; i++) {
  309. ans = std::min(ans, std::fabs(gety(p[i], ss, half) - p[i].y));
  310. }
  311. for (int i = , sz = half.size(); i < sz - ; i++) {
  312. P pp = ss[i];
  313. ans = std::min(ans, std::fabs(getyy(pp, p) - pp.y));
  314. }
  315. printf("%.3f\n", ans);
  316. return ;
  317. }

1039. [ZJOI2008]无序运动Movement

平移、旋转、放缩对两个相似三角形没有影响,那么一个长度为 $n$ 的轨迹就可以描述为 $n-2$ 个三角形,每个三角形就用相邻两边长来描述,还得加上第二条线段在第一条线段的逆时针还是顺时针方向,因为如果不加这个就会出现翻不翻转带来的影响,然后就变成了字符串匹配了。不过由于字符集很大,得用 map 来存边。然后翻转一下再做一遍。
如果一个轨迹共线的话,翻转后他会被重新算一遍,所以要除以 $2$。
如果一个轨迹长度小于 $3$ 的话, 他就能匹配上所有长度相等的子串。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. char buf[ << ], buf2[ << ], a[], *p1 = buf, *p2 = buf, hh = '\n';
  5. int p, p3 = -;
  6. void read() {}
  7. void print() {}
  8. inline int getc() {
  9. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, , << , stdin), p1 == p2) ? EOF : *p1++;
  10. }
  11. inline void flush() {
  12. fwrite(buf2, , p3 + , stdout), p3 = -;
  13. }
  14. template <typename T, typename... T2>
  15. inline void read(T &x, T2 &... oth) {
  16. T f = ; x = ;
  17. char ch = getc();
  18. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getc(); }
  19. while (isdigit(ch)) { x = x * + ch - ; ch = getc(); }
  20. x *= f;
  21. read(oth...);
  22. }
  23. template <typename T, typename... T2>
  24. inline void print(T x, T2... oth) {
  25. if (p3 > << ) flush();
  26. if (x < ) buf2[++p3] = , x = -x;
  27. do {
  28. a[++p] = x % + ;
  29. } while (x /= );
  30. do {
  31. buf2[++p3] = a[p];
  32. } while (--p);
  33. buf2[++p3] = hh;
  34. print(oth...);
  35. }
  36. }
  37.  
  38. struct P {
  39. int x, y;
  40. void read() { IO::read(x, y); }
  41. void print() { printf("%d %d\n", x, y); }
  42. P() {}
  43. P(int x, int y): x(x), y(y) {}
  44. P operator + (const P &p) const { return P(x + p.x, y + p.y); }
  45. P operator - (const P &p) const { return P(x - p.x, y - p.y); }
  46. int det(const P &p) const { return x * p.y - y * p.x; }
  47. int abs2() { return x * x + y * y; }
  48. };
  49.  
  50. struct Node {
  51. int a, b, c, dir;
  52. bool operator < (const Node &p) const {
  53. if (a != p.a) return a < p.a;
  54. if (b != p.b) return b < p.b;
  55. if (c != p.c) return c < p.c;
  56. return dir < p.dir;
  57. }
  58. bool operator == (const Node &p) const {
  59. return !(*this < p) && !(p < *this);
  60. }
  61. };
  62.  
  63. const int N = 2e5 + ;
  64. int n, m, par[N], tol, ans[N];
  65. std::vector<P> point[N], all;
  66. std::vector<int> flag[N];
  67. std::map<Node, int> mp[N];
  68.  
  69. int gcd(int a, int b) {
  70. while (b) {
  71. a %= b;
  72. std::swap(a, b);
  73. }
  74. return a;
  75. }
  76.  
  77. Node getnode(const P &A, const P &B, const P &C) {
  78. int lena = (B - A).abs2(), lenb = (B - C).abs2(), lenc = (A - C).abs2();
  79. int g = gcd(lena, gcd(lenb, lenc));
  80. lena /= g, lenb /= g, lenc /= g;
  81. int crs = ;
  82. if ((B - A).det(C - B) > ) crs = ;
  83. else if ((B - A).det(C - B) < ) crs = -;
  84. return {lena, lenb, lenc, crs};
  85. }
  86.  
  87. void done(std::vector<P> vec, int id) {
  88. if (vec.size() < ) {
  89. ans[id] = n - vec.size() + ;
  90. return;
  91. }
  92. par[id] = ;
  93. int rt = ;
  94. for (int i = ; i < vec.size() - ; i++) {
  95. Node o = getnode(vec[i], vec[i + ], vec[i + ]);
  96. if (o.dir) par[id] = ;
  97. auto it = mp[rt].find(o);
  98. if (it == mp[rt].end()) {
  99. mp[rt][o] = ++tol;
  100. rt = tol;
  101. } else {
  102. rt = it->second;
  103. }
  104. }
  105. flag[rt].push_back(id);
  106. }
  107.  
  108. int fail[N], last[N], cnt[N];
  109.  
  110. void build() {
  111. std::queue<int> que;
  112. for (auto it: mp[])
  113. que.push(it.second);
  114. while (!que.empty()) {
  115. int u = que.front(); que.pop();
  116. for (auto it: mp[u]) {
  117. Node cur_node = it.first;
  118. int f = fail[u], v = it.second;
  119. for (; f && mp[f].find(cur_node) == mp[f].end(); f = fail[f]);
  120. if (mp[f].find(cur_node) != mp[f].end())
  121. f = mp[f][cur_node];
  122. fail[v] = f;
  123. last[v] = flag[fail[v]].empty() ? last[fail[v]] : fail[v];
  124. que.push(v);
  125. }
  126. }
  127. }
  128.  
  129. void solve(const std::vector<P> &vec) {
  130. int rt = ;
  131. for (int i = ; i < n - ; i++) {
  132. Node node = getnode(vec[i], vec[i + ], vec[i + ]);
  133. for (; rt && mp[rt].find(node) == mp[rt].end(); rt = fail[rt]);
  134. if (mp[rt].find(node) != mp[rt].end())
  135. rt = mp[rt][node];
  136. for (int j = rt; j; j = last[j])
  137. ++cnt[j];
  138. }
  139. }
  140.  
  141. int main() {
  142. IO::read(n, m);
  143. for (int i = ; i <= m; i++) {
  144. int k;
  145. IO::read(k);
  146. point[i].resize(k);
  147. for (int j = ; j < k; j++)
  148. point[i][j].read();
  149. done(point[i], i);
  150. }
  151. build();
  152. all.resize(n);
  153. for (int i = ; i < n; i++)
  154. all[i].read();
  155. solve(all);
  156. for (int i = ; i < n; i++)
  157. all[i].y *= -;
  158. solve(all);
  159. for (int i = ; i <= tol; i++)
  160. for (int u: flag[i])
  161. ans[u] += cnt[i] / (par[u] + );
  162. for (int i = ; i <= m; i++)
  163. IO::print(ans[i]);
  164. IO::flush();
  165. return ;
  166. }

1040. [ZJOI2008]骑士

基环森林上DP。
我刚开始想的就是找到环,然后把环上每个点及它的子树缩成一个点,就变成一个环上的DP了。然后就是强制第一个不取和强制最后一个不取。
看了别人的题解发现可以不用那么麻烦,只要找到环上任意相邻的两点,强制把这条边断开,然后还是DP两次就行了。
DP方程就比较naive了。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. char buf[ << ], buf2[ << ], a[], *p1 = buf, *p2 = buf, hh = '\n';
  5. int p, p3 = -;
  6. void read() {}
  7. void print() {}
  8. inline int getc() {
  9. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, , << , stdin), p1 == p2) ? EOF : *p1++;
  10. }
  11. inline void flush() {
  12. fwrite(buf2, , p3 + , stdout), p3 = -;
  13. }
  14. template <typename T, typename... T2>
  15. inline void read(T &x, T2 &... oth) {
  16. T f = ; x = ;
  17. char ch = getc();
  18. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getc(); }
  19. while (isdigit(ch)) { x = x * + ch - ; ch = getc(); }
  20. x *= f;
  21. read(oth...);
  22. }
  23. template <typename T, typename... T2>
  24. inline void print(T x, T2... oth) {
  25. if (p3 > << ) flush();
  26. if (x < ) buf2[++p3] = , x = -x;
  27. do {
  28. a[++p] = x % + ;
  29. } while (x /= );
  30. do {
  31. buf2[++p3] = a[p];
  32. } while (--p);
  33. buf2[++p3] = hh;
  34. print(oth...);
  35. }
  36. }
  37.  
  38. #define ll long long
  39. const int N = 1e6 + ;
  40. int n, fa[N], root, _root;
  41. ll dp[N][], val[N];
  42. bool vis[N];
  43. int head[N];
  44. struct E {
  45. int v, ne;
  46. } e[N << ];
  47. int cnt = , ban;
  48.  
  49. void add(int u, int v) {
  50. e[++cnt].v = v; e[cnt].ne = head[u]; head[u] = cnt;
  51. }
  52.  
  53. void dfs(int u, int f) {
  54. vis[u] = ;
  55. for (int i = head[u]; i; i = e[i].ne) {
  56. int v = e[i].v;
  57. if (v == f) continue;
  58. if (vis[v]) {
  59. root = u, _root = v;
  60. ban = i;
  61. } else {
  62. dfs(v, u);
  63. }
  64. }
  65. }
  66.  
  67. void DP(int u, int f) {
  68. dp[u][] = ; dp[u][] = val[u];
  69. for (int i = head[u]; i; i = e[i].ne) {
  70. int v = e[i].v;
  71. if (v == f || i == ban || i == (ban ^ )) continue;
  72. DP(v, u);
  73. dp[u][] += std::max(dp[v][], dp[v][]);
  74. dp[u][] += dp[v][];
  75. }
  76. }
  77.  
  78. int main() {
  79. IO::read(n);
  80. for (int i = , u; i <= n; i++)
  81. IO::read(val[i], u), add(u, i), add(i, u);
  82. ll ans = ;
  83. for (int i = ; i <= n; i++) if (!vis[i]) {
  84. dfs(i, -);
  85. DP(root, -);
  86. ll temp = dp[root][];
  87. DP(_root, -);
  88. temp = std::max(temp, dp[_root][]);
  89. ans += temp;
  90. }
  91. printf("%lld\n", ans);
  92. return ;
  93. }

1041. [HAOI2008]圆上的整点

$x^2+y^2=r^2$
$x^2=(r-y)(r+y)$
$(\frac{x}{g})^2=\frac{r-y}{g}\frac{r+y}{g}$
其中 $g = \gcd(r-y,r+y)$
那么 $\frac{r-y}{g}$ 和 $\frac{r+y}{g}$ 互质
并且左边 $(\frac{x}{g})^2$ 是完全平方数,那么右边 $\frac{r-y}{g}$ 和 $\frac{r+y}{g}$ 也必须都是完全平方数
设 $a^2=\frac{r-y}{g}$,$b^2=\frac{r+y}{g}$
$a^2+b^2=\frac{2r}{g}$
枚举 $g$ 之后再枚举 $a$,可以做到复杂度 $O(n^{\frac{3}{4}})$

  1. #include <bits/stdc++.h>
  2.  
  3. #define ll long long
  4.  
  5. template<class T>
  6. T gcd(T a, T b) {
  7. while (b) {
  8. a %= b;
  9. std::swap(a, b);
  10. }
  11. return a;
  12. }
  13.  
  14. int main() {
  15. ll r;
  16. scanf("%lld", &r);
  17. int ans = ;
  18. for (ll g = ; g * g <= * r; g++) {
  19. if (( * r) % g) continue;
  20. for (ll a = ; a * a < g / ; a++) {
  21. ll b = sqrt(g - a * a + 0.5);
  22. if (a * a + b * b == g && gcd(a, b) == ) ans++;
  23. }
  24. for (ll a = ; a * a < r / g; a++) {
  25. ll b = sqrt( * r / g - a * a + 0.5);
  26. if (a * a + b * b == * r / g && gcd(a, b) == ) ans++;
  27. }
  28. }
  29. printf("%d\n", ans * + );
  30. return ;
  31. }

1042. [HAOI2008]硬币购物

涨芝士了!
可以先做一次完全背包,然后对每次询问容斥得到答案。
限制用 $d_i$ 个 $c_i$ 硬币,就相当于减去至少用了 $d_i+1$ 个 $c_i$ 硬币,那么就是假设我强制花了 $(d_i+1)*c_i$ 块钱,然后方案就是 $f[s-(d_i+1)*c_i]$。容斥就暴力枚举 $2^4$ 钟情况即可。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = 1e5 + ;
  5. ll dp[N];
  6. int w[], T, d[];
  7.  
  8. int get(int x) {
  9. int cnt = ;
  10. while (x) {
  11. cnt++;
  12. x &= (x - );
  13. }
  14. return cnt;
  15. }
  16.  
  17. int main() {
  18. for (int i = ; i < ; i++)
  19. scanf("%d", w + i);
  20. scanf("%d", &T);
  21. dp[] = ;
  22. for (int i = ; i < ; i++)
  23. for (int j = w[i]; j <= N - ; j++)
  24. dp[j] += dp[j - w[i]];
  25. while (T--) {
  26. for (int i = ; i < ; i++)
  27. scanf("%d", d + i);
  28. int s;
  29. scanf("%d", &s);
  30. ll ans = ;
  31. for (int i = ; i < ; i++) {
  32. int sz = get(i), sum = s;
  33. int c = sz & ? - : ;
  34. for (int j = ; j < ; j++)
  35. if (i >> j & )
  36. sum -= w[j] * (d[j] + );
  37. if (sum >= )
  38. ans += c * dp[sum];
  39. }
  40. printf("%lld\n", ans);
  41. }
  42. return ;
  43. }

1044. [HAOI2008]木棍分割

第一问二分傻逼题。

第二问前缀和优化一下DP

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 5e4 + ;
  4. const int MOD = ;
  5. int n, m, a[N], dp[N][], sum[N];
  6. int dp_sum[N][];
  7.  
  8. void M(int &x) {
  9. if (x >= MOD) x -= MOD;
  10. if (x < ) x += MOD;
  11. }
  12.  
  13. inline bool chkmax(int &a, int b) {
  14. return a < b ? a = b, : ;
  15. }
  16.  
  17. inline bool chkmin(int &a, int b) {
  18. return a > b ? a = b, : ;
  19. }
  20.  
  21. bool check(int mid) {
  22. int cnt = , sum = ;
  23. for (int i = ; i <= n; i++) {
  24. if (sum + a[i] > mid) {
  25. cnt++;
  26. sum = a[i];
  27. } else {
  28. sum += a[i];
  29. }
  30. }
  31. return cnt <= m;
  32. }
  33.  
  34. int que[N];
  35.  
  36. int main() {
  37. scanf("%d%d", &n, &m);
  38. m++;
  39. int l = , r = ;
  40. for (int i = ; i <= n; i++)
  41. scanf("%d", a + i), chkmax(l, a[i]), r += a[i], sum[i] = sum[i - ] + a[i];
  42. int ans = ;
  43. while (l <= r) {
  44. int mid = l + r >> ;
  45. if (check(mid)) ans = mid, r = mid - ;
  46. else l = mid + ;
  47. }
  48. for (int i = ; i <= n; i++)
  49. dp[i][] = (sum[i] <= ans ? : ), M(dp_sum[i][] = dp_sum[i - ][] + dp[i][]);
  50. int ans2 = dp[n][];
  51. for (int j = ; j <= m; j++) {
  52. int cur = j & ;
  53. int last = ;
  54. for (int i = ; i <= n; i++) {
  55. dp[i][cur] = ;
  56. while (i > last && sum[i] - sum[last] > ans) last++;
  57. M(dp[i][cur] += dp_sum[i - ][cur ^ ] - dp_sum[std::max(last - , )][cur ^ ]);
  58. M(dp_sum[i][cur] = dp_sum[i - ][cur] + dp[i][cur]);
  59. }
  60.  
  61. M(ans2 += dp[n][cur]);
  62. }
  63. printf("%d %d\n", ans, ans2);
  64. return ;
  65. }

1045. [HAOI2008] 糖果传递

设 $x_i$ 表示第 $i$ 个人向第 $i+1$ 个人传的糖果数。
那么
$$\begin{cases}
x_n - x_1 = \overline{a}-a_1\\
x_1 - x_2 = \overline{a}-a_2\\
x_2 - x_3 = \overline{a}-a_3\\
\dots
\end{cases}
$$
再做一下前缀和,能得到
$x_n-x_i=\sum_{j=1}^i(\overline{a}-a_i)$
即 $x_i = x_n - \sum_{j=1}^i(\overline{a}-a_i)$
答案就是 $\sum_{i=1}^n |x_i|$
就是数轴上有 $n$ 个点,要找一个点到他们的距离之和最小,大概就是中位数吧,因为中位数往左往右移增加的距离会比少的距离多一部分,举个例子就知道了。

  1. #include <bits/stdc++.h>
  2. typedef long long ll;
  3.  
  4. const int N = 1e6 + ;
  5. ll a[N];
  6.  
  7. int main() {
  8. int n;
  9. scanf("%d", &n);
  10. ll sum = ;
  11. for (int i = ; i <= n; i++)
  12. scanf("%lld", a + i), sum += a[i];
  13. sum /= n;
  14. for (int i = ; i <= n; i++)
  15. a[i] = a[i - ] + sum - a[i];
  16. std::sort(a + , a + + n);
  17. sum = a[(n + ) / ];
  18. ll ans = ;
  19. for (int i = ; i <= n; i++)
  20. ans += std::abs(a[i] - sum);
  21. printf("%lld\n", ans);
  22. return ;
  23. }

1046. [HAOI2007]上升序列

先求出 $dp[i]$ 表示以 $i$ 开头的最长下降子序列。

然后因为是 $x$ 序列字典序最小,那就从左往右填就行了。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e4 + ;
  4.  
  5. int cnt, a[N], v[N], n;
  6. int dp[N];
  7.  
  8. struct Bit {
  9. int tree[N];
  10. inline int lowbit(int x) {
  11. return x & -x;
  12. }
  13. void add(int x, int v) {
  14. for (int i = x; i <= n; i += lowbit(i))
  15. tree[i] = std::max(tree[i], v);
  16. }
  17. int query(int x) {
  18. int ans = ;
  19. for (int i = x; i; i -= lowbit(i))
  20. ans = std::max(ans, tree[i]);
  21. return ans;
  22. }
  23. } bit;
  24.  
  25. int main() {
  26. scanf("%d", &n);
  27. for (int i = ; i <= n; i++)
  28. scanf("%d", a + i), v[i] = a[i];
  29. std::sort(v + , v + + n);
  30. cnt = std::unique(v + , v + + n) - v - ;
  31. int ans = ;
  32. for (int i = n; i >= ; i--) {
  33. a[i] = std::lower_bound(v + , v + + cnt, a[i]) - v;
  34. a[i] = cnt - a[i] + ;
  35. dp[i] = bit.query(a[i] - ) + ;
  36. bit.add(a[i], dp[i]);
  37. ans = std::max(ans, dp[i]);
  38. a[i] = cnt - a[i] + ;
  39. }
  40. int m;
  41. scanf("%d", &m);
  42. for (int i = ; i <= m; i++) {
  43. int len;
  44. scanf("%d", &len);
  45. if (len > ans) {
  46. puts("Impossible");
  47. continue;
  48. }
  49. int last = ;
  50. for (int i = ; i <= n; i++) {
  51. if (!len) break;
  52. if (dp[i] >= len && a[i] > last) {
  53. printf("%d%c", v[a[i]], " \n"[len == ]);
  54. len--;
  55. last = a[i];
  56. }
  57. }
  58. }
  59. return ;
  60. }

1047. [HAOI2007]理想的正方形

先对每一行做一遍单调队列,然后再对每一列做一遍单调队列即可。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. char buf[ << ], buf2[ << ], a[], *p1 = buf, *p2 = buf, hh = '\n';
  5. int p, p3 = -;
  6. void read() {}
  7. void print() {}
  8. inline int getc() {
  9. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, , << , stdin), p1 == p2) ? EOF : *p1++;
  10. }
  11. inline void flush() {
  12. fwrite(buf2, , p3 + , stdout), p3 = -;
  13. }
  14. template <typename T, typename... T2>
  15. inline void read(T &x, T2 &... oth) {
  16. T f = ; x = ;
  17. char ch = getc();
  18. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getc(); }
  19. while (isdigit(ch)) { x = x * + ch - ; ch = getc(); }
  20. x *= f;
  21. read(oth...);
  22. }
  23. template <typename T, typename... T2>
  24. inline void print(T x, T2... oth) {
  25. if (p3 > << ) flush();
  26. if (x < ) buf2[++p3] = , x = -x;
  27. do {
  28. a[++p] = x % + ;
  29. } while (x /= );
  30. do {
  31. buf2[++p3] = a[p];
  32. } while (--p);
  33. buf2[++p3] = hh;
  34. print(oth...);
  35. }
  36. }
  37.  
  38. const int N = ;
  39. int A[N][N], a, b, n, mx[N][N], mn[N][N];
  40.  
  41. int main() {
  42. IO::read(a, b, n);
  43. for (int i = ; i <= a; i++)
  44. for (int j = ; j <= b; j++)
  45. IO::read(A[i][j]);
  46. static int que1[N], que2[N];
  47. for (int i = ; i <= a; i++) {
  48. int l1 = , r1 = , l2 = , r2 = ;
  49. for (int j = ; j <= b; j++) {
  50. {
  51. while (l1 < r1 && que1[l1] <= j - n) l1++;
  52. while (l1 < r1 && A[i][que1[r1 - ]] >= A[i][j]) r1--;
  53. que1[r1++] = j;
  54. if (j >= n) mn[i][j] = A[i][que1[l1]];
  55. }
  56. {
  57. while (l2 < r2 && que2[l2] <= j - n) l2++;
  58. while (l2 < r2 && A[i][que2[r2 - ]] <= A[i][j]) r2--;
  59. que2[r2++] = j;
  60. if (j >= n) mx[i][j] = A[i][que2[l2]];
  61. }
  62. }
  63. }
  64. int ans = 2e9;
  65. for (int j = n; j <= b; j++) {
  66. int l1 = , r1 = , l2 = , r2 = ;
  67. for (int i = ; i <= a; i++) {
  68. {
  69. while (l1 < r1 && que1[l1] <= i - n) l1++;
  70. while (l1 < r1 && mn[que1[r1 - ]][j] >= mn[i][j]) r1--;
  71. que1[r1++] = i;
  72. }
  73. {
  74. while (l2 < r2 && que2[l2] <= i - n) l2++;
  75. while (l2 < r2 && mx[que2[r2 - ]][j] <= mx[i][j]) r2--;
  76. que2[r2++] = i;
  77. }
  78. if (i >= n) ans = std::min(ans, mx[que2[l2]][j] - mn[que1[l1]][j]);
  79. }
  80. }
  81. printf("%d\n", ans);
  82. return ;
  83. }

1048. [HAOI2007]分割矩阵

记忆化着搜就行了,$f[x_1][y_1][x_2][y_2][c]$ 表示矩阵 $(x_1,y_1)$,$(x_2,y_2)$ 分成 $c$ 份的最小方差和。

  1. #include <bits/stdc++.h>
  2.  
  3. typedef double db;
  4.  
  5. const db eps = 1e-;
  6. const int N = ;
  7. db f[N][N][N][N][N];
  8. int sum[N][N];
  9. db ave;
  10. int A[N][N];
  11.  
  12. int sign(db x) {
  13. if (fabs(x) < eps) return ;
  14. return x > ? : -;
  15. }
  16.  
  17. db sqr(db x) {
  18. return x * x;
  19. }
  20.  
  21. db Sum(int x1, int x2, int y1, int y2) {
  22. return sum[x2][y2] + sum[x1 - ][y1 - ] - sum[x1 - ][y2] - sum[x2][y1 - ];
  23. }
  24.  
  25. db solve(int x1, int x2, int y1, int y2, int c) {
  26. db &ans = f[x1][x2][y1][y2][c];
  27. if (ans != -) return ans;
  28. if (c == ) return ans = sqr(Sum(x1, x2, y1, y2) - ave);
  29. ans = 1e9;
  30. for (int i = x1; i < x2; i++)
  31. for (int cc = ; cc < c; cc++)
  32. ans = std::min(ans, solve(x1, i, y1, y2, cc) + solve(i + , x2, y1, y2, c - cc));
  33. for (int i = y1; i < y2; i++)
  34. for (int cc = ; cc < c; cc++)
  35. ans = std::min(ans, solve(x1, x2, y1, i, cc) + solve(x1, x2, i + , y2, c - cc));
  36. return ans;
  37. }
  38.  
  39. int n, m, c;
  40.  
  41. int main() {
  42. scanf("%d%d%d", &n, &m, &c);
  43. for (int i = ; i <= n; i++)
  44. for (int j = ; j <= n; j++)
  45. for (int k = ; k <= m; k++)
  46. for (int l = ; l <= m; l++)
  47. for (int o = ; o <= c; o++)
  48. f[i][j][k][l][o] = -;
  49. for (int i = ; i <= n; i++)
  50. for (int j = ; j <= m; j++)
  51. scanf("%d", &A[i][j]), sum[i][j] = sum[i - ][j] + sum[i][j - ] - sum[i - ][j - ] + A[i][j];
  52. ave = 1.0 * sum[n][m] / c;
  53. printf("%.2f\n", sqrt(solve(, n, , m, c) / c));
  54. return ;
  55. }

1049. [HAOI2006]数字序列

第一问套路,每一个数字减去它的下标就是求最长不下降子序列了。

第二问,$g[i]$ 表示 $1$ 到 $i$ 都已经符合条件的最小花费

那么可以列出方程 $g[i]=\min \{g[j]+cost(j+1,i)\},dp[j]+1=dp[i]$

cost 有个结论就是存在一个分割点 $k$,左边的值都变成 $b[j]$,右边的值都变成 $b[i]$ 最优,然后暴力求就行了,因为数据随机

  1. #include <bits/stdc++.h>
  2.  
  3. typedef long long ll;
  4. const int N = 4e4 + ;
  5. const ll INF = 0x3f3f3f3f3f3f3f3f;
  6. const int inf = 0x3f3f3f3f;
  7.  
  8. template<class T>
  9. inline bool chkmax(T &a, T b) {
  10. return a < b ? a = b, : ;
  11. }
  12.  
  13. template<class T>
  14. inline bool chkmin(T &a, T b) {
  15. return a > b ? a = b, : ;
  16. }
  17.  
  18. int n, a[N], dp[N], v[N], cnt, b[N];
  19. std::vector<int> vec[N];
  20. ll g[N], s1[N], s2[N];
  21.  
  22. struct BIT {
  23. int tree[N];
  24. inline int lowbit(int x) {
  25. return x & -x;
  26. }
  27. void add(int x, int v) {
  28. for (int i = x; i <= cnt; i += lowbit(i))
  29. chkmax(tree[i], v);
  30. }
  31. int query(int x) {
  32. int ans = ;
  33. for (int i = x; i; i -= lowbit(i))
  34. chkmax(ans, tree[i]);
  35. return ans;
  36. }
  37. } bit;
  38.  
  39. int main() {
  40. scanf("%d", &n);
  41. for (int i = ; i <= n; i++)
  42. scanf("%d", a + i), b[i] = a[i] - i, v[i] = b[i];
  43. a[++n] = inf, b[n] = a[n] - n, v[n] = b[n];
  44. std::sort(v + , v + + n);
  45. cnt = std::unique(v + , v + + n) - v - ;
  46. int ans = ;
  47. for (int i = ; i <= n; i++) {
  48. b[i] = std::lower_bound(v + , v + + cnt, b[i]) - v;
  49. dp[i] = bit.query(b[i]) + ;
  50. bit.add(b[i], dp[i]);
  51. chkmax(ans, dp[i]);
  52. }
  53. ans = n - ans;
  54. printf("%d\n", ans);
  55. for (int i = ; i <= n; i++) {
  56. vec[dp[i]].push_back(i);
  57. b[i] = v[b[i]];
  58. g[i] = INF;
  59. }
  60. g[] = ; b[] = -inf;
  61. for (int i = ; i <= n; i++)
  62. for (int j = , sz = vec[dp[i] - ].size(); j < sz; j++) {
  63. int pos = vec[dp[i] - ][j];
  64. if (pos > i) break;
  65. if (b[pos] > b[i]) continue;
  66. for (int k = pos; k <= i; k++)
  67. s1[k] = std::abs(b[k] - b[pos]), s2[k] = std::abs(b[i] - b[k]);
  68. for (int k = pos + ; k <= i; k++)
  69. s1[k] += s1[k - ], s2[k] += s2[k - ];
  70. for (int k = pos; k <= i; k++)
  71. chkmin(g[i], g[pos] + s1[k] - s1[pos] + s2[i] - s2[k]);
  72. }
  73. printf("%lld\n", g[n]);
  74. return ;
  75. }

1188. [HNOI2007]分裂游戏

每次操作相当于在一个位置取一个豆子,放到后面的两个位置上,即 $i$ -> $(j, k)$。那么每个豆子只跟位置有关,而与它所在的那一堆有多少豆子无关。游戏的和就是所有豆子的 SG 函数的异或和。

那么一个位置有奇数个豆子时对游戏的和有贡献,理解起来就是如果一个位置有两个豆子的话,第二个人可以模仿第一个人的动作。

SG 函数可以倒着求出来,然后枚举第一步即可。

这里不是一堆石子是单个游戏,而是每个石子都是单个游戏。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. int a[N], SG[N];
  5. bool vis[N];
  6.  
  7. int main() {
  8. int T;
  9. scanf("%d", &T);
  10. while (T--) {
  11. int n;
  12. scanf("%d", &n);
  13. for (int i = ; i <= n; i++)
  14. scanf("%d", a + i), SG[i] = ;
  15. for (int i = n - ; i; i--) {
  16. memset(vis, , sizeof(vis));
  17. for (int j = i + ; j <= n; j++)
  18. for (int k = j; k <= n; k++)
  19. vis[SG[j] ^ SG[k]] = ;
  20. for (int j = ; ; j++)
  21. if (!vis[j]) {
  22. SG[i] = j;
  23. break;
  24. }
  25. }
  26. int A = , B = , C = , cnt = ;
  27. int ans = ;
  28. for (int i = ; i <= n; i++)
  29. if (a[i] & )
  30. ans ^= SG[i];
  31. for (int i = ; i <= n; i++) if (a[i])
  32. for (int j = i + ; j <= n; j++)
  33. for (int k = j; k <= n; k++)
  34. if (!(ans ^ SG[i] ^ SG[j] ^ SG[k])) {
  35. if (!cnt) A = i, B = j, C = k;
  36. cnt++;
  37. }
  38. printf("%d %d %d\n%d\n", A - , B - , C - , cnt);
  39. }
  40. return ;
  41. }

1791. [Ioi2008]Island 岛屿

求基环森林的直径之和。
这个刚好是个基环内向树,那么可以拓扑排序做,之后再拆环,单调队列优化一下DP。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. namespace IO {
  5. void read() {}
  6. template<class T, class ... T2>
  7. inline void read(T &x, T2 &... oth) {
  8. x = ; T f = ; char ch = getchar();
  9. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getchar(); }
  10. while (isdigit(ch)) x = x * + ch - '', ch = getchar();
  11. x *= f;
  12. read(oth...);
  13. }
  14. }
  15.  
  16. const int N = 1e6 + ;
  17. int to[N], w[N], n, in[N];
  18. ll f[N], dp[N], d[N];
  19. bool vis[N];
  20.  
  21. struct Deque {
  22. static const int LEN = 1e6;
  23. int l, r, que[LEN];
  24. Deque() { l = r = ; }
  25. void clear() { l = r = ; }
  26. bool empty() { return !(l ^ r); }
  27. void push_back(int v) { que[r++] = v; r %= LEN; }
  28. void push_front(int v) { que[l = (l - + LEN) % LEN] = v; }
  29. void pop_front() { ++l; l %= LEN; }
  30. void pop_back() { r = (r - + LEN) % LEN; }
  31. int front() { return que[l]; }
  32. int back() { return que[r - ]; }
  33. } que;
  34.  
  35. template<class T>
  36. inline bool chkmax(T &a, T b) {
  37. return a < b ? a = b, : ;
  38. }
  39.  
  40. int cur[N * ];
  41. ll s[N * ];
  42.  
  43. int main() {
  44. IO::read(n);
  45. for (int i = ; i <= n; i++) {
  46. IO::read(to[i], w[i]);
  47. in[to[i]]++;
  48. }
  49. for (int i = ; i <= n; i++)
  50. if (!in[i]) que.push_back(i);
  51. while (!que.empty()) {
  52. int u = que.front(); que.pop_front();
  53. int v = to[u];
  54. if (!--in[v]) que.push_back(v);
  55. // 经过 v 的最长路径
  56. chkmax(f[v], d[u] + d[v] + w[u]);
  57. // 从 v 出发的最长路径
  58. chkmax(d[v], d[u] + w[u]);
  59. // v 的子树中最长路径
  60. chkmax(dp[v], std::max(dp[u], f[v]));
  61. }
  62. ll ans = ;
  63. for (int i = ; i <= n; i++) if (in[i] && !vis[i]) {
  64. int cnt = ;
  65. ll temp = ;
  66. for (int j = i; !vis[j]; j = to[j]) {
  67. cur[++cnt] = j;
  68. vis[j] = ;
  69. chkmax(temp, dp[j]);
  70. }
  71. int dcnt = cnt << ;
  72. for (int i = cnt + ; i <= dcnt; i++)
  73. cur[i] = cur[i - cnt];
  74. que.clear();
  75. for (int j = ; j <= dcnt; j++) {
  76. s[j] = s[j - ] + w[cur[j - ]];
  77. while (!que.empty() && j - que.front() >= cnt) que.pop_front();
  78. if (!que.empty())
  79. chkmax(temp, d[cur[j]] + d[cur[que.front()]] + s[j] - s[que.front()]);
  80. while (!que.empty() && d[cur[j]] - s[j] > d[cur[que.back()]] - s[que.back()])
  81. que.pop_back();
  82. que.push_back(j);
  83. }
  84. ans += temp;
  85. }
  86. printf("%lld\n", ans);
  87. return ;
  88. }

2038. [2009国家集训队]小Z的袜子(hose)

莫队板子题。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 5e4 + ;
  4.  
  5. int n, m, B, ans[N][], fz, fm;
  6. int cnt[N], len, col[N];
  7.  
  8. struct Q {
  9. int l, r, id;
  10. bool operator < (const Q &p) const {
  11. return l / B == p.l / B ? r < p.r : l < p.l;
  12. }
  13. } q[N];
  14.  
  15. void add(int x) {
  16. fz += cnt[x];
  17. cnt[x]++;
  18. fm += len;
  19. len++;
  20. }
  21.  
  22. void del(int x) {
  23. cnt[x]--;
  24. fz -= cnt[x];
  25. len--;
  26. fm -= len;
  27. }
  28.  
  29. int main() {
  30. scanf("%d%d", &n, &m);
  31. for (int i = ; i <= n; i++)
  32. scanf("%d", col + i);
  33. for (int i = ; i <= m; i++)
  34. scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
  35. B = sqrt(n);
  36. std::sort(q + , q + + m);
  37. int l = , r = ;
  38. for (int i = ; i <= m; i++) {
  39. while (l > q[i].l) add(col[--l]);
  40. while (l < q[i].l) del(col[l++]);
  41. while (r > q[i].r) del(col[r--]);
  42. while (r < q[i].r) add(col[++r]);
  43. if (fm == ) {
  44. ans[q[i].id][] = ans[q[i].id][] = ;
  45. continue;
  46. }
  47. int g = std::__gcd(fz, fm);
  48. ans[q[i].id][] = fz / g;
  49. ans[q[i].id][] = fm / g;
  50. }
  51. for (int i = ; i <= m; i++)
  52. printf("%d/%d\n", ans[i][], ans[i][]);
  53. return ;
  54. }

2120. 数颜色

带修莫队板子题。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e4 + ;
  4. int cnt[N * ], ans, B, a[N];
  5. int n, m;
  6.  
  7. struct Q {
  8. int l, r, t, id;
  9. bool operator < (const Q &p) const {
  10. if (l / B != p.l / B) return l < p.l;
  11. if (r / B != p.r / B) return r < p.r;
  12. return t < p.t;
  13. }
  14. } q[N];
  15.  
  16. struct C {
  17. int p, col;
  18. } change[N];
  19.  
  20. void add(int x) {
  21. ++cnt[x];
  22. if (cnt[x] == )
  23. ans++;
  24. }
  25.  
  26. void del(int x) {
  27. --cnt[x];
  28. if (cnt[x] == )
  29. ans--;
  30. }
  31.  
  32. void update(int cur_q, int cur_c) {
  33. if (change[cur_c].p >= q[cur_q].l && change[cur_c].p <= q[cur_q].r)
  34. del(a[change[cur_c].p]), add(change[cur_c].col);
  35. std::swap(a[change[cur_c].p], change[cur_c].col);
  36. }
  37.  
  38. int res[N];
  39.  
  40. int main() {
  41. char s[];
  42. scanf("%d%d", &n, &m);
  43. B = n / sqrt(m * / );
  44. for (int i = ; i <= n; i++)
  45. scanf("%d", a + i);
  46. int qcnt = , ccnt = ;
  47. for (int i = ; i <= m; i++) {
  48. scanf("%s", s);
  49. if (s[] == 'Q') {
  50. ++qcnt;
  51. scanf("%d%d", &q[qcnt].l, &q[qcnt].r);
  52. q[qcnt].t = ccnt;
  53. q[qcnt].id = qcnt;
  54. } else {
  55. ++ccnt;
  56. scanf("%d%d", &change[ccnt].p, &change[ccnt].col);
  57. }
  58. }
  59. int l = , r = , cur = ;
  60. std::sort(q + , q + + qcnt);
  61. for (int i = ; i <= qcnt; i++) {
  62. while (l < q[i].l) del(a[l++]);
  63. while (l > q[i].l) add(a[--l]);
  64. while (r < q[i].r) add(a[++r]);
  65. while (r > q[i].r) del(a[r--]);
  66. while (cur < q[i].t) update(i, ++cur);
  67. while (cur > q[i].t) update(i, cur--);
  68. res[q[i].id] = ans;
  69. }
  70. for (int i = ; i <= qcnt; i++)
  71. printf("%d\n", res[i]);
  72. }

2154. Crash的数字表格

$$\sum_{i = 1}^n \sum_{j= 1}^m lcm(i, j)=\sum_{i=1}^n\sum_{j=1}^m \dfrac{i*j}{(i,j)}$$
$$=\sum_{i=1}^n\sum_{j=1}^m \sum_d \dfrac{ij}{d}[(\frac{i}{d}, \frac{j}{d})==1]=\sum_{d}d\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}ij[(i,j)==1]$$
$$=\sum_d d\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor \frac{m}{d}\rfloor}ij \sum_{p|(i,j)}\mu(p)=\sum_d d\sum_p p^2\mu(p)\sum_{i=1}^{\lfloor \frac{n}{dp}\rfloor}i\sum_{j=1}^{\lfloor \frac{m}{dp}\rfloor}j$$
$$=\sum_dd\sum_pp^2\mu(p)(\dfrac{(\lfloor \frac{n}{dp} \rfloor + 1)\lfloor \frac{n}{dp} \rfloor}{2})(\dfrac{(\lfloor \frac{m}{dp} \rfloor + 1)\lfloor \frac{m}{dp} \rfloor}{2})$$
$$=\sum_T(\dfrac{(\lfloor \frac{n}{T} \rfloor + 1)\lfloor \frac{n}{T} \rfloor}{2})(\dfrac{(\lfloor \frac{m}{T} \rfloor + 1)\lfloor \frac{m}{T} \rfloor}{2})\sum_{p|T}\dfrac{T}{p}p^2\mu(p)$$
$$=\sum_T(\dfrac{(\lfloor \frac{n}{T} \rfloor + 1)\lfloor \frac{n}{T} \rfloor}{2})(\dfrac{(\lfloor \frac{m}{T} \rfloor + 1)\lfloor \frac{m}{T} \rfloor}{2})\sum_{p|T}Tp\mu(p)$$
需要预处理 $\sum_{d|n}nd\mu(d)$,只需要预处理 $f(n)=\sum_{d|n}d\mu(d)$,$f(n)$ 为积性函数,且 $f(p^t) = 1 - p$。
所以这道题可以搞成多组询问的。
复杂度为 $O(n+q\sqrt n)$

  1. #include <bits/stdc++.h>
  2.  
  3. const int MOD = ;
  4. const int N = 1e7 + ;
  5. int prime[N], prin;
  6. int f[N];
  7. bool vis[N];
  8.  
  9. void M(int &x) {
  10. if (x >= MOD) x -= MOD;
  11. if (x < ) x += MOD;
  12. }
  13.  
  14. void init(int N) {
  15. f[] = ;
  16. for (int i = ; i < N; i++) {
  17. if (!vis[i]) {
  18. prime[++prin] = i;
  19. M(f[i] = - i);
  20. }
  21. for (int j = ; j <= prin && i * prime[j] < N; j++) {
  22. vis[i * prime[j]] = ;
  23. if (i % prime[j] == ) {
  24. int temp = i;
  25. while (temp % prime[j] == ) temp /= prime[j];
  26. f[i * prime[j]] = 1LL * ( - prime[j] + MOD) * f[temp] % MOD;
  27. break;
  28. }
  29. f[i * prime[j]] = 1LL * f[i] * f[prime[j]] % MOD;
  30. }
  31. }
  32. for (int i = ; i < N; i++)
  33. M(f[i] = (f[i - ] + 1LL * f[i] * i % MOD));
  34. }
  35.  
  36. int cal(int n, int m) {
  37. int x = 1LL * n * (n + ) / % MOD;
  38. int y = 1LL * m * (m + ) / % MOD;
  39. return 1LL * x * y % MOD;
  40. }
  41.  
  42. int solve(int n, int m) {
  43. if (n > m) std::swap(n, m);
  44. int ans = ;
  45. for (int i = , j; i <= n; i = j + ) {
  46. j = std::min(n / (n / i), m / (m / i));
  47. M(ans += 1LL * cal(n / i, m / i) * (f[j] - f[i - ] + MOD) % MOD);
  48. }
  49. return ans;
  50. }
  51.  
  52. signed main() {
  53. int n, m;
  54. scanf("%d%d", &n, &m);
  55. init(std::min(n, m) + );
  56. printf("%d\n", solve(n, m));
  57. return ;
  58. }

2179. FFT快速傅立叶

FFT板子

  1. #include <bits/stdc++.h>
  2.  
  3. struct Complex {
  4. double r, i;
  5. Complex(double r = 0.0, double i = 0.0): r(r), i(i) {}
  6. Complex operator + (const Complex &p) const { return Complex(r + p.r, i + p.i); }
  7. Complex operator - (const Complex &p) const { return Complex(r - p.r, i - p.i); }
  8. Complex operator * (const Complex &p) const { return Complex(r * p.r - i * p.i, r * p.i + i * p.r); }
  9. };
  10.  
  11. const double pi = acos(-1.0);
  12. const int N = 2e5 + ;
  13. int n, limit, r[N], l;
  14. int v[N], A[N], B[N], C[N];
  15. Complex a[N], b[N], c[N];
  16.  
  17. void FFT(Complex *a, int pd) {
  18. for (int i = ; i < limit; i++)
  19. if (i < r[i])
  20. std::swap(a[i], a[r[i]]);
  21. for (int mid = ; mid < limit; mid <<= ) {
  22. Complex wn = Complex(cos(pi / mid), pd * sin(pi / mid));
  23. for (int l = mid << , j = ; j < limit; j += l) {
  24. Complex w = Complex(1.0, 0.0);
  25. for (int k = ; k < mid; k++, w = w * wn) {
  26. Complex u = a[k + j], v = w * a[k + j + mid];
  27. a[k + j] = u + v;
  28. a[k + j + mid] = u - v;
  29. }
  30. }
  31. }
  32. if (pd == -)
  33. for (int i = ; i < limit; i++)
  34. a[i] = Complex(a[i].r / limit, a[i].i / limit);
  35. }
  36.  
  37. int main() {
  38. scanf("%d", &n);
  39. for (int i = ; i < n; i++) {
  40. int x;
  41. scanf("%d", &x);
  42. x += ;
  43. A[x]++;
  44. B[x * ]++;
  45. C[x * ]++;
  46. }
  47. for (int i = ; i <= ; i++)
  48. a[i] = Complex((double)A[i], 0.0);
  49. for (int i = ; i <= ; i++)
  50. b[i] = Complex((double)B[i], 0.0);
  51. limit = ;
  52. while (limit <= + )
  53. limit <<= , l++;
  54. for (int i = ; i < limit; i++)
  55. r[i] = r[i >> ] >> | ((i & ) << (l - ));
  56. FFT(a, );
  57. FFT(b, );
  58. for (int i = ; i < limit; i++)
  59. b[i] = b[i] * a[i];
  60. for (int i = ; i < limit; i++)
  61. a[i] = a[i] * a[i] * a[i];
  62. FFT(a, -);
  63. FFT(b, -);
  64. for (int i = ; i <= ; i++) {
  65. long long ans = (long long)((a[i].r - 3.0 * b[i].r + 2.0 * C[i]) / 6.0 + 0.5);
  66. if (ans > )
  67. printf("%d : %lld\n", i - , ans);
  68. }
  69. return ;
  70. }

2238. Mst

先求出 Mst,如果要删的边不在 Mst 上,那么对答案不影响。

如果删的边 $(u, v)$ 在 Mst 上,相当于把树分成以 $u$ 为根和以 $v$ 为根两棵树,现在就要在两棵树里挑出一条权值最小的非树边。

预处理每一条非树边,它们之间的路径的边的值取个min,那么就树剖一下,建线段树,区间里面比这个大的数都变成这个数。

我以为要吉司机线段树,但是查询是单点查询,标记永久化即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e5 + ;
  4. inline bool chkmax(int &a, int b) { return a < b ? a = b, : ; }
  5. inline bool chkmin(int &a, int b) { return a > b ? a = b, : ; }
  6.  
  7. struct Edge {
  8. int u, v, w, id;
  9. bool operator < (const Edge &p) const { return this->w < p.w; }
  10. } edge[N], sort_edge[N];
  11.  
  12. int n, m, sz[N], son[N], fa[N], top[N];
  13. int dep[N], dfn[N], tol;
  14. bool vis[N];
  15. std::vector<int> vec[N];
  16.  
  17. void dfs1(int u, int f) {
  18. fa[u] = f;
  19. sz[u] = ;
  20. dep[u] = dep[f] + ;
  21. for (int v: vec[u]) {
  22. if (v == f) continue;
  23. dfs1(v, u);
  24. sz[u] += sz[v];
  25. if (sz[son[u]] < sz[v]) son[u] = v;
  26. }
  27. }
  28.  
  29. void dfs2(int u, int tp) {
  30. top[u] = tp;
  31. dfn[u] = ++tol;
  32. if (!son[u]) return;
  33. dfs2(son[u], tp);
  34. for (int v: vec[u])
  35. if (v != fa[u] && v != son[u])
  36. dfs2(v, v);
  37. }
  38.  
  39. const int INF = 0x3f3f3f3f;
  40.  
  41. struct Seg {
  42. #define lp p << 1
  43. #define rp p << 1 | 1
  44. int tree[N << ];
  45. void build(int p, int l, int r) {
  46. tree[p] = INF;
  47. if (l == r) return;
  48. int mid = l + r >> ;
  49. build(lp, l, mid);
  50. build(rp, mid + , r);
  51. }
  52. void update(int p, int l, int r, int x, int y, int z) {
  53. if (x <= l && y >= r) {
  54. chkmin(tree[p], z);
  55. return;
  56. }
  57. int mid = l + r >> ;
  58. if (x <= mid) update(lp, l, mid, x, y, z);
  59. if (y > mid) update(rp, mid + , r, x, y, z);
  60. }
  61. int query(int p, int l, int r, int pos) {
  62. if (l == r) return tree[p];
  63. int mid = l + r >> ;
  64. if (pos <= mid) return std::min(tree[p], query(lp, l, mid, pos));
  65. return std::min(tree[p], query(rp, mid + , r, pos));
  66. }
  67. } seg;
  68.  
  69. void solve(int u, int v, int w) {
  70. while (top[u] != top[v]) {
  71. if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
  72. seg.update(, , n, dfn[top[u]], dfn[u], w);
  73. u = fa[top[u]];
  74. }
  75. if (dep[u] > dep[v]) std::swap(u, v);
  76. seg.update(, , n, dfn[u] + , dfn[v], w);
  77. }
  78.  
  79. struct DSU {
  80. int fa[N];
  81. void init(int n) { for (int i = ; i <= n; i++) fa[i] = i; }
  82. int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
  83. bool merge(int u, int v) {
  84. u = find(u), v = find(v);
  85. if (u == v) return ;
  86. fa[u] = v;
  87. return ;
  88. }
  89. } dsu;
  90.  
  91. int main() {
  92. scanf("%d%d", &n, &m);
  93. for (int i = ; i <= m; i++) {
  94. scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w);
  95. edge[i].id = i;
  96. sort_edge[i] = edge[i];
  97. }
  98. dsu.init(n);
  99. std::sort(sort_edge + , sort_edge + + m);
  100. int ed = , ans = ;
  101. for (int i = ; i <= m; i++) {
  102. int u = sort_edge[i].u, v = sort_edge[i].v;
  103. if (!dsu.merge(u, v)) continue;
  104. vec[u].push_back(v); vec[v].push_back(u);
  105. ed++;
  106. ans += sort_edge[i].w;
  107. vis[sort_edge[i].id] = ;
  108. }
  109. int q;
  110. scanf("%d", &q);
  111. if (ed < n - ) {
  112. while (q--) {
  113. int x;
  114. scanf("%d", &x);
  115. puts("Not connected");
  116. }
  117. return ;
  118. }
  119. dfs1(, );
  120. dfs2(, );
  121. seg.build(, , n);
  122. assert(n == tol);
  123. for (int i = ; i <= m; i++) {
  124. if (vis[i]) continue;
  125. solve(edge[i].u, edge[i].v, edge[i].w);
  126. }
  127. while (q--) {
  128. int i;
  129. scanf("%d", &i);
  130. if (!vis[i]) {
  131. printf("%d\n", ans);
  132. continue;
  133. }
  134. int u = dep[edge[i].u] < dep[edge[i].v] ? edge[i].v : edge[i].u;
  135. int temp = seg.query(, , n, dfn[u]);
  136. if (temp == INF) puts("Not connected");
  137. else printf("%d\n", ans - edge[i].w + temp);
  138. }
  139. return ;
  140. }

2301. [HAOI2011]Problem b

询问拆成四个,就像矩阵数点一样。
每一个询问的形式为 $\sum_{i=1}^n\sum_{j=1}^m[(i,j)==k]$。
$$\sum_{i=1}^n\sum_{j=1}^m[(i,j)==k]=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}[(i,j)==1]$$
把 $\lfloor \dfrac{n}{k} \rfloor$ 换成 $n$,把 $\lfloor \dfrac{m}{k} \rfloor$ 换成 $m$
$$\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|(i,j)}\mu(d)=\sum_d \mu(d)\sum_{i=1}^{n}[d|i]\sum_{j=1}^{m}[d|j]$$
$$=\sum_d\mu(d)\lfloor \dfrac{n}{d}\rfloor\lfloor \dfrac{m}{d}\rfloor$$
求个 $\mu$ 的前缀和加数论分块。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 5e4 + ;
  4. int prime[N], prin, mu[N];
  5. bool vis[N];
  6.  
  7. void init() {
  8. mu[] = ;
  9. for (int i = ; i < N; i++) {
  10. if (!vis[i]) prime[++prin] = i, mu[i] = -;
  11. for (int j = ; j <= prin && i * prime[j] < N; j++) {
  12. vis[i * prime[j]] = ;
  13. if (i % prime[j] == ) {
  14. mu[i * prime[j]] = ;
  15. break;
  16. }
  17. mu[i * prime[j]] = -mu[i];
  18. }
  19. }
  20. for (int i = ; i < N; i++)
  21. mu[i] += mu[i - ];
  22. }
  23.  
  24. int solve(int n, int m) {
  25. int res = ;
  26. for (int i = , j; i <= std::min(n, m); i = j + ) {
  27. j = std::min(n / (n / i), m / (m / i));
  28. res += (mu[j] - mu[i - ]) * (n / i) * (m / i);
  29. }
  30. return res;
  31. }
  32.  
  33. int main() {
  34. init();
  35. int n;
  36. scanf("%d", &n);
  37. while (n--) {
  38. int a, b, c, d, k;
  39. scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
  40. printf("%d\n", solve(b / k, d / k) - solve(b / k, (c - ) / k) - solve((a - ) / k, d / k) + solve((a - ) / k, (c - ) / k));
  41. }
  42. return ;
  43. }

2693. jzptab

$$\sum_{i=1}^n\sum_{j=1}^m \text{lcm}(i, j)$$
$$=\sum_{i=1}^n\sum_{j=1}^m \frac{ij}{\text{gcd}(i,j)}$$
$$=\sum_{d}d\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}ij[(i,j)=1]$$
$$=\sum_{d}d\sum_{d'}\mu(d')d'^2 s(\lfloor\frac{n}{dd'}\rfloor)s(\lfloor\frac{m}{dd'}\rfloor)$$
$$=\sum_{T} s(\lfloor\frac{n}{T}\rfloor)s(\lfloor\frac{m}{T}\rfloor)\sum_{d|T}\mu(d)d^2\frac{T}{d}$$$$=\sum_{T} s(\lfloor\frac{n}{T}\rfloor)s(\lfloor\frac{m}{T}\rfloor)T\sum_{d|T}\mu(d)d$$
其中 $s(n) = \frac{n\times (n+1)}{2}$,设 $f(n)=\sum_{d|n}d\mu(d)$,$g(n)=nf(n)$,$f(n)$ 是积性函数,可以直接筛,问题解决。

  1. #include <bits/stdc++.h>
  2.  
  3. const int MOD = ;
  4. const int N = 1e7 + ;
  5. int prime[N], prin;
  6. int f[N];
  7. bool vis[N];
  8.  
  9. void M(int &x) {
  10. if (x >= MOD) x -= MOD;
  11. if (x < ) x += MOD;
  12. }
  13.  
  14. void init(int N) {
  15. f[] = ;
  16. for (int i = ; i < N; i++) {
  17. if (!vis[i]) {
  18. prime[++prin] = i;
  19. M(f[i] = - i + MOD);
  20. }
  21. for (int j = ; j <= prin && i * prime[j] < N; j++) {
  22. vis[i * prime[j]] = ;
  23. if (i % prime[j] == ) {
  24. int temp = i;
  25. while (temp % prime[j] == ) temp /= prime[j];
  26. f[i * prime[j]] = 1LL * ( - prime[j] + MOD) * f[temp] % MOD;
  27. break;
  28. }
  29. f[i * prime[j]] = 1LL * f[i] * f[prime[j]] % MOD;
  30. }
  31. }
  32. for (int i = ; i < N; i++)
  33. f[i] = (1LL * f[i - ] + 1LL * f[i] * i % MOD) % MOD;
  34. }
  35.  
  36. int cal(int n, int m) {
  37. int x = 1LL * n * (n + ) / % MOD;
  38. int y = 1LL * m * (m + ) / % MOD;
  39. return 1LL * x * y % MOD;
  40. }
  41.  
  42. int solve(int n, int m) {
  43. if (n > m) std::swap(n, m);
  44. int ans = ;
  45. for (int i = , j; i <= n; i = j + ) {
  46. j = std::min(n / (n / i), m / (m / i));
  47. (ans += 1LL * cal(n / i, m / i) * (f[j] - f[i - ] + MOD) % MOD) %= MOD;
  48. }
  49. return ans;
  50. }
  51.  
  52. signed main() {
  53. int T;
  54. scanf("%d", &T);
  55. init(N - );
  56. while (T--) {
  57. int n, m;
  58. scanf("%d%d", &n, &m);
  59. printf("%d\n", solve(n, m));
  60. }
  61. return ;
  62. }

2820. YY的GCD

$$\sum_{i=1}^{n}\sum_{i=1}^{m}\left[\left(i,j\right)\in P\right]$$
$$=\sum_{p\in P}\sum_{i=1}^{n}\sum_{i=1}^{m}\left[\left(i,j\right)=p\right]$$$$=\sum_{p\in P}\sum_{i=1}^{\lfloor \frac{n}{p} \rfloor}\sum_{i=1}^{\lfloor \frac{m}{p} \rfloor}\left[\left(i,j\right)=1\right]$$
$$=\sum_{p\in P}\sum_{d=1}^{\min\{\lfloor \frac{n}{p} \rfloor,\lfloor \frac{m}{p} \rfloor\}}\mu(d)\lfloor\frac{n}{pd}\rfloor\lfloor\frac{m}{pd}\rfloor$$
$$=\sum_{T=1}^{\min\{n, m\}}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{p|T \wedge p\in P}\mu(\frac{T}{p})$$

$f(n) = \sum_{p|n \wedge p \in P}\mu(\frac{n}{p})$ 不是积性函数,但是可以线性筛预处理出来。
对于质数 $p$,$f(p)=1$
当 $p < M(i)$ 时,$f(i \times p)=-f(i)+\mu(i)$
当 $p=M(i)$ 时,$i \times p$ 里含有 $p^2$,所以 $\forall p' \neq p$,$\mu(\frac{i\times p}{p'})=0$,所以 $f(i\times p)=\mu(i)$

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = 1e7 + ;
  5. int prime[N], prin, mu[N];
  6. bool vis[N];
  7. ll f[N];
  8.  
  9. void init() {
  10. mu[] = ;
  11. for (int i = ; i < N; i++) {
  12. if (!vis[i]) {
  13. prime[++prin] = i;
  14. mu[i] = -;
  15. f[i] = ;
  16. }
  17. for (int j = ; j <= prin && i * prime[j] < N; j++) {
  18. vis[i * prime[j]] = ;
  19. if (i % prime[j] == ) {
  20. mu[i * prime[j]] = ;
  21. f[i * prime[j]] = mu[i];
  22. break;
  23. }
  24. mu[i * prime[j]] = -mu[i];
  25. f[i * prime[j]] = -f[i] + mu[i];
  26. }
  27. }
  28. for (int i = ; i < N; i++)
  29. f[i] += f[i - ];
  30. }
  31.  
  32. ll solve(int n, int m) {
  33. ll ans = ;
  34. for (int i = , j; i <= std::min(n, m); i = j + ) {
  35. j = std::min(n / (n / i), m / (m / i));
  36. ans += (f[j] - f[i - ]) * (n / i) * (m / i);
  37. }
  38. return ans;
  39. }
  40.  
  41. int main() {
  42. init();
  43. int T;
  44. scanf("%d", &T);
  45. while (T--) {
  46. int n, m;
  47. scanf("%d%d", &n, &m);
  48. printf("%lld\n", solve(n, m));
  49. }
  50. return ;
  51. }

2940. [Poi2000]条纹

就是枚举放一种条纹下去会变成啥样,要么变成两段要么变成一段,求下 SG 函数就好了。

复杂度 $O(n ^ 2)$

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = ;
  4. int SG[N];
  5. bool vis[N];
  6.  
  7. int main() {
  8. int a, b, c;
  9. scanf("%d%d%d", &a, &b, &c);
  10. for (int i = std::min(a, std::min(b, c)); i <= ; i++) {
  11. memset(vis, , sizeof(vis));
  12. for (int j = ; j <= i - a; j++)
  13. vis[SG[j] ^ SG[i - a - j]] = ;
  14. for (int j = ; j <= i - b; j++)
  15. vis[SG[j] ^ SG[i - b - j]] = ;
  16. for (int j = ; j <= i - c; j++)
  17. vis[SG[j] ^ SG[i - c - j]] = ;
  18. for (int j = ; ; j++)
  19. if (!vis[j]) {
  20. SG[i] = j;
  21. break;
  22. }
  23. }
  24. int T;
  25. scanf("%d", &T);
  26. while (T--) {
  27. int n;
  28. scanf("%d", &n);
  29. puts(SG[n] ? "" : "");
  30. }
  31. return ;
  32. }

3529. [Sdoi2014]数表 

$$\sum_{i=1}^{n}\sum_{j=1}^{m}\sigma_{1}(\gcd(i, j))[\sigma_1(\gcd(i,j))\leq a]$$
首先忽略 $\sigma_1(\gcd(i,j))\leq a$ 的限制
即求$$\sum_{i=1}^{n}\sum_{j=1}^{m}\sigma_{1}(\gcd(i, j))$$
$$=\sum_{d=1}^{\min\{n,m\}}\sigma_{1}(d)\sum_{i=1}^{n}\sum_{j=1}^{m}[(i,j)=d]$$
$$=\sum_{d=1}^{\min\{n,m\}}\sigma_{1}(d)\sum_{d'=1}^{\min\{\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor\}}\mu(d')\lfloor\frac{n}{dd'}\rfloor\lfloor\frac{m}{dd'}\rfloor$$
$$=\sum_{T}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{d|T}\sigma_1(d)\mu(\frac{T}{d})$$
设 $f(n)=\sum_{d|n}\sigma_1(d)\mu(\frac{n}{d})$,要维护这个的前缀和,会受 $\sigma_1(\gcd(i,j))\leq a$ 的就只有这里的 $\sigma_1(d)$,离线一下询问,用树状数组维护这个前缀和,然后就像扫描线一样去修改这个前缀和,总共需要修改 $n\log n$ 次,所以复杂度为 $O(n\log ^2 n)$

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. char buf[ << ], buf2[ << ], a[], *p1 = buf, *p2 = buf, hh = '\n';
  5. int p, p3 = -;
  6. void read() {}
  7. void print() {}
  8. inline int getc() {
  9. return p1 == p2 && (p2 = (p1 = buf) + fread(buf, , << , stdin), p1 == p2) ? EOF : *p1++;
  10. }
  11. inline void flush() {
  12. fwrite(buf2, , p3 + , stdout), p3 = -;
  13. }
  14. template <typename T, typename... T2>
  15. inline void read(T &x, T2 &... oth) {
  16. T f = ; x = ;
  17. char ch = getc();
  18. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getc(); }
  19. while (isdigit(ch)) { x = x * + ch - ; ch = getc(); }
  20. x *= f;
  21. read(oth...);
  22. }
  23. template <typename T, typename... T2>
  24. inline void print(T x, T2... oth) {
  25. if (p3 > << ) flush();
  26. if (x < ) buf2[++p3] = , x = -x;
  27. do {
  28. a[++p] = x % + ;
  29. } while (x /= );
  30. do {
  31. buf2[++p3] = a[p];
  32. } while (--p);
  33. buf2[++p3] = hh;
  34. print(oth...);
  35. }
  36. } // using namespace IO
  37.  
  38. const int N = 1e5;
  39. int prime[N + ], prin, mu[N + ], n, d[N + ];
  40. bool vis[N + ];
  41. std::pair<int, int> p[N + ];
  42.  
  43. void init() {
  44. mu[] = ;
  45. for (int i = ; i <= N; i++) {
  46. if (!vis[i]) {
  47. prime[++prin] = i;
  48. mu[i] = -;
  49. }
  50. for (int j = ; j <= prin && i * prime[j] <= N; j++) {
  51. vis[i * prime[j]] = ;
  52. if (i % prime[j] == ) break;
  53. mu[i * prime[j]] = -mu[i];
  54. }
  55. }
  56. for (int i = ; i <= N; i++)
  57. for (int j = ; 1LL * i * j <= N; j++)
  58. d[i * j] += i;
  59. for (int i = ; i <= N; i++) {
  60. p[i].first = d[i], p[i].second = i;
  61. }
  62. std::sort(p + , p + + N);
  63. }
  64.  
  65. struct Bit {
  66. unsigned tree[N];
  67. int lowbit(int x) {
  68. return x & -x;
  69. }
  70. void add(int x, unsigned v) {
  71. for (int i = x; i <= N; i += lowbit(i))
  72. tree[i] += v;
  73. }
  74. unsigned query(int x) {
  75. unsigned ans = ;
  76. for (int i = x; i; i -= lowbit(i))
  77. ans += tree[i];
  78. return ans;
  79. }
  80. } bit;
  81.  
  82. struct QUERY{
  83. int n, m, a, id;
  84. bool operator < (const QUERY &p) const {
  85. return a < p.a;
  86. }
  87. } que[N];
  88.  
  89. unsigned ans[N];
  90.  
  91. unsigned solve(int n, int m) {
  92. unsigned ans = ;
  93. for (int i = , j; i <= std::min(n, m); i = j + ) {
  94. j = std::min(n / (n / i), m / (m / i));
  95. ans += 1u * (n / i) * (m / i) * (bit.query(j) - bit.query(i - ));
  96. }
  97. return ans;
  98. }
  99.  
  100. int main() {
  101. init();
  102. int q;
  103. IO::read(q);
  104. for (int i = ; i <= q; i++) {
  105. IO::read(que[i].n, que[i].m, que[i].a);
  106. que[i].id = i;
  107. }
  108. std::sort(que + , que + + q);
  109. int cur = ;
  110. for (int i = ; i <= q; i++) {
  111. while (cur <= N && p[cur].first <= que[i].a) {
  112. for (int j = ; j * p[cur].second <= N; j++)
  113. bit.add(j * p[cur].second, p[cur].first * mu[j]);
  114. cur++;
  115. }
  116. ans[que[i].id] = solve(que[i].n, que[i].m);
  117. }
  118. for (int i = ; i <= q; i++)
  119. IO::print(ans[i] & ((1u << ) - ));
  120. IO::flush();
  121. return ;
  122. }

3576. [Hnoi2014]江南乐

有一个很显然的 $O(n^2)$ 的方法。枚举要分成 $i$ 份,然后大堆是 $n % i$,小堆有 $n - n \% i$ 的奇偶性,就可以求出 SG 函数。

但其实很多情况是冗余的,小堆的大小只有 $\sqrt n$ 种,即 $\lfloor \dfrac {n}{i}\rfloor$。

然后因为在同一段中 $n \% i$ 和 $n \% (i + 2)$ 的奇偶性相同,所以只需要求一下 $n \% i$ 和 $n \% (i + 1)$ 的情况即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e5 + ;
  4. int SG[N], vis[N], T, F;
  5. std::vector<int> a[];
  6.  
  7. int get(int n) {
  8. for (int i = , j, s; i <= n; i = j + ) {
  9. j = n / (n / i);
  10. s = n / i;
  11. int y = n % i, x = i - y;
  12. vis[((x & ) * SG[s]) ^ ((y & ) * SG[s + ])] = n;
  13. if (i < j) {
  14. y = n % (i + );
  15. x = i + - y;
  16. vis[((x & ) * SG[s]) ^ ((y & ) * SG[s + ])] = n;
  17. }
  18. }
  19. for (int i = ; ; i++)
  20. if (vis[i] != n)
  21. return i;
  22. }
  23.  
  24. void init(int n) {
  25. for (int i = F; i <= n; i++)
  26. SG[i] = get(i);
  27. }
  28.  
  29. int main() {
  30. scanf("%d%d", &T, &F);
  31. int mx = ;
  32. for (int i = ; i <= T; i++) {
  33. int n;
  34. scanf("%d", &n);
  35. a[i].resize(n);
  36. for (int j = ; j < n; j++)
  37. scanf("%d", &a[i][j]), mx = std::max(mx, a[i][j]);
  38. }
  39. init(mx);
  40. for (int i = ; i <= T; i++) {
  41. int n = a[i].size();
  42. int ans = ;
  43. for (int j = ; j < n; j++) {
  44. int x = a[i][j];
  45. ans ^= SG[x];
  46. }
  47. printf("%d%c", ans ? : , " \n"[i == T]);
  48. }
  49. return ;
  50. }

3722. 精神污染

查询每一条路径覆盖了多少条路径。

一条路径 $(u, v)$,如果另一条路径 $(u_0, v_0)$ 被其覆盖,那么 $u_0$ 和 $v_0$ 一定在这条路径上。

那么可以以dfs序建主席树,每个节点代表一个版本的线段树,一条路径就将 $v$ 加到 $u$ 版本的线段树上。

在 $in[v]$ 处 +1, $out[v]$ 处 -1。

查询的时候就是差分查询,在 $root[u], root[v], root[lca], root[fa_lca]$ 查询 $(lca, u)$ + $(lca, v)$ - $(lca, lca)$ - $1$

毒瘤卡空间,得用树剖求LCA

  1. #include <bits/stdc++.h>
  2. #define pii pair<int, int>
  3. #define fi first
  4. #define se second
  5. #define ll long long
  6.  
  7. const int N = 1e5 + ;
  8. int n, m, in[N], out[N], fa[N], sz[N], son[N], dep[N], root[N], cnt, top[N];
  9. std::vector<int> vec[N], query[N];
  10. std::pii edge[N];
  11.  
  12. ll gcd(ll a, ll b) {
  13. while (b) {
  14. a %= b;
  15. std::swap(a, b);
  16. }
  17. return a;
  18. }
  19.  
  20. struct Seg {
  21. struct Node {
  22. int lp, rp, sum;
  23. } tree[N * ];
  24. int tol;
  25. void update(int &p, int q, int l, int r, int pos, int v) {
  26. tree[p = ++tol] = tree[q];
  27. tree[p].sum += v;
  28. if (l == r) return;
  29. int mid = l + r >> ;
  30. if (pos <= mid) update(tree[p].lp, tree[q].lp, l, mid, pos, v);
  31. else update(tree[p].rp, tree[q].rp, mid + , r, pos, v);
  32. }
  33. int query(int p, int q, int f, int ff, int l, int r, int x, int y) {
  34. if (x <= l && y >= r)
  35. return tree[p].sum + tree[q].sum - tree[f].sum - tree[ff].sum;
  36. int mid = l + r >> ;
  37. ll ans = ;
  38. if (x <= mid) ans += query(tree[p].lp, tree[q].lp, tree[f].lp, tree[ff].lp, l, mid, x, y);
  39. if (y > mid) ans += query(tree[p].rp, tree[q].rp, tree[f].rp, tree[ff].rp, mid + , r, x, y);
  40. return ans;
  41. }
  42. } seg;
  43.  
  44. void dfs1(int u, int f = ) {
  45. fa[u] = f;
  46. dep[u] = dep[f] + ;
  47. sz[u] = ;
  48. in[u] = ++cnt;
  49. for (int v: vec[u])
  50. if (v != f) {
  51. dfs1(v, u);
  52. sz[u] += sz[v];
  53. if (sz[v] > sz[son[u]]) son[u] = v;
  54. }
  55. out[u] = ++cnt;
  56. }
  57.  
  58. void dfs2(int u, int tp) {
  59. top[u] = tp;
  60. if (!son[u]) return;
  61. dfs2(son[u], tp);
  62. for (int v: vec[u])
  63. if (v != fa[u] && v != son[u])
  64. dfs2(v, v);
  65. }
  66.  
  67. void dfs(int u, int f = ) {
  68. root[u] = root[f];
  69. for (int v: query[u]) {
  70. seg.update(root[u], root[u], , cnt, in[v], );
  71. seg.update(root[u], root[u], , cnt, out[v], -);
  72. }
  73. for (int v: vec[u])
  74. if (v != f) dfs(v, u);
  75. }
  76.  
  77. int Lca(int u, int v) {
  78. while (top[u] != top[v]) {
  79. if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
  80. u = fa[top[u]];
  81. }
  82. if (dep[u] > dep[v]) std::swap(u, v);
  83. return u;
  84. }
  85.  
  86. int main() {
  87. freopen("in.txt", "r", stdin);
  88. //freopen("out1.txt", "w", stdout);
  89. scanf("%d%d", &n, &m);
  90. for (int i = ; i < n; i++) {
  91. int u, v;
  92. scanf("%d%d", &u, &v);
  93. vec[u].push_back(v);
  94. vec[v].push_back(u);
  95. }
  96. for (int i = ; i <= m; i++) {
  97. scanf("%d%d", &edge[i].fi, &edge[i].se);
  98. query[edge[i].fi].push_back(edge[i].se);
  99. }
  100. std::sort(edge + , edge + + m);
  101. dfs1();
  102. dfs2(, );
  103. dfs();
  104. ll ans = ;
  105. for (int i = ; i <= m; i++) {
  106. int u = edge[i].fi, v = edge[i].se, f = Lca(u, v), ff = fa[f];
  107. //printf("%d %d %d\n", u, v, f);
  108. ans += seg.query(root[u], root[v], root[f], root[ff], , cnt, in[f], in[u]);
  109. ans += seg.query(root[u], root[v], root[f], root[ff], , cnt, in[f], in[v]);
  110. ans -= seg.query(root[u], root[v], root[f], root[ff], , cnt, in[f], in[f]);
  111. ans--;
  112. }
  113. //printf("%lld\n", ans);
  114. long long all = 1LL * m * (m - ) / ;
  115. long long g = gcd(all, ans);
  116. printf("%lld/%lld\n", ans / g, all / g);
  117. return ;
  118. }

3781. 小B的询问

莫队。

  1. #include <bits/stdc++.h>
  2. #define ll long long
  3.  
  4. const int N = 5e4 + ;
  5. int cnt[N], a[N];
  6. ll res, ans[N];
  7. int B;
  8.  
  9. struct Q {
  10. int l, r, id;
  11. bool operator < (const Q &p) const {
  12. return l / B == p.l / B ? r < p.r : l < p.l;
  13. }
  14. } q[N];
  15.  
  16. void add(int x) {
  17. res -= 1LL * cnt[x] * cnt[x];
  18. cnt[x]++;
  19. res += 1LL * cnt[x] * cnt[x];
  20. }
  21.  
  22. void del(int x) {
  23. res -= 1LL * cnt[x] * cnt[x];
  24. cnt[x]--;
  25. res += 1LL * cnt[x] * cnt[x];
  26. }
  27.  
  28. int main() {
  29. int n, m, k;
  30. scanf("%d%d%d", &n, &m, &k);
  31. B = n / sqrt(m);
  32. for (int i = ; i <= n; i++)
  33. scanf("%d", a + i);
  34. for (int i = ; i <= m; i++)
  35. scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
  36. std::sort(q + , q + + m);
  37. int l = , r = ;
  38. for (int i = ; i <= m; i++) {
  39. while (l < q[i].l) del(a[l++]);
  40. while (l > q[i].l) add(a[--l]);
  41. while (r > q[i].r) del(a[r--]);
  42. while (r < q[i].r) add(a[++r]);
  43. ans[q[i].id] = res;
  44. }
  45. for (int i = ; i <= m; i++)
  46. printf("%lld\n", ans[i]);
  47. return ;
  48. }

3994. [SDOI2015]约数个数和

求 $$\sum_{i=1}^n\sum_{j=1}^md(ij)$$
$$d(ij)=\sum_{x|i}\sum_{y|j}[(i,j)==1]=\sum_{x|i}\sum_{y|j}\sum_{p|(x,y)}\mu(p)$$
$$=\sum_p \mu(p)\sum_{x|i}[d|x]\sum_{y|j}[d|y]=\sum_{p|i,p|j}\mu(p)d(\frac{i}{p})d(\frac{j}{p})$$
代回原式得
$$\sum_{i=1}^n\sum_{j=1}^m\sum_{p|i,p|j}\mu(p)d(\frac{i}{p})d(\frac{j}{p})=\sum_{p}\mu(p)\sum_{p|i}d(\frac{i}{p})\sum_{p|j}d(\frac{j}{p})$$
$$=\sum_{p}\mu(p)\sum_{i=1}^{\lfloor \frac{n}{p} \rfloor}d(i)\sum_{j=1}^{\lfloor \frac{m}{p} \rfloor}d(j)$$
令 $s(n)=\sum_{i=1}^n d(i)$
原式为$$\sum_{p}\mu(p)s(\lfloor \frac{n}{p} \rfloor)s(\lfloor \frac{m}{p} \rfloor)$$

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 5e4 + ;
  4. int mu[N], prime[N], prin, d[N], t[N];
  5. bool vis[N];
  6.  
  7. void init(int n) {
  8. mu[] = d[] = t[] = ;
  9. for (int i = ; i <= n; i++) {
  10. if (!vis[i]) {
  11. prime[++prin] = i;
  12. d[i] = ;
  13. t[i] = ;
  14. mu[i] = -;
  15. }
  16. for (int j = ; j <= prin && i * prime[j] < N; j++) {
  17. vis[i * prime[j]] = ;
  18. if (i % prime[j] == ) {
  19. t[i * prime[j]] = t[i] + ;
  20. mu[i * prime[j]] = ;
  21. d[i * prime[j]] = d[i] / (t[i] + ) * (t[i] + );
  22. break;
  23. }
  24. d[i * prime[j]] = d[i] * ;
  25. t[i * prime[j]] = ;
  26. mu[i * prime[j]] = -mu[i];
  27. }
  28. }
  29. for (int i = ; i <= n; i++)
  30. mu[i] += mu[i - ], d[i] += d[i - ];
  31. }
  32.  
  33. #define ll long long
  34.  
  35. ll solve(int n, int m) {
  36. ll ans = ;
  37. if (n > m) std::swap(n, m);
  38. for (int i = , j; i <= n; i = j + ) {
  39. j = std::min(n / (n / i), m / (m / i));
  40. ans += 1LL * (mu[j] - mu[i - ]) * 1LL * d[n / i] * d[m / i];
  41. }
  42. return ans;
  43. }
  44.  
  45. int main() {
  46. init(N - );
  47. int T;
  48. scanf("%d", &T);
  49. while (T--) {
  50. int n, m;
  51. scanf("%d%d", &n, &m);
  52. printf("%lld\n", solve(n, m));
  53. }
  54. return ;
  55. }

4025. 二分图

线段树分治+按秩合并的并查集解决加边删边的问题。
一个图是二分图当且仅当点数大于等于二并且不存在奇环。
那么可以用带权并查集维护路径长度,会出现环就是当加入一条边是产生环并且原路径长度为偶数。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. void read() {}
  5. template<class T, class... T2>
  6. void read(T &x, T2 &... oth) {
  7. x = ; T f = ; char ch = getchar();
  8. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getchar(); }
  9. while (isdigit(ch)) x = x * + ch - '', ch = getchar();
  10. x *= f;
  11. read(oth...);
  12. }
  13. }
  14.  
  15. const int N = 2e5 + ;
  16.  
  17. int st[N], top;
  18.  
  19. struct DSU {
  20. int fa[N], d[N], dep[N];
  21. void init(int n) {
  22. for (int i = ; i <= n; i++)
  23. fa[i] = i, d[i] = dep[i] = ;
  24. }
  25. int getfa(int x) {
  26. while (x != fa[x])
  27. x = fa[x];
  28. return x;
  29. }
  30. int getdis(int x) {
  31. int dis = ;
  32. while (x != fa[x])
  33. dis ^= d[x], x = fa[x];
  34. return dis;
  35. }
  36. bool merge(int x, int y) {
  37. int D = getdis(x) ^ getdis(y) ^ ;
  38. x = getfa(x), y = getfa(y);
  39. if (x == y) return D == ;
  40. if (dep[x] < dep[y])
  41. std::swap(x, y);
  42. if (dep[x] == dep[y])
  43. dep[x]++, st[++top] = -x;
  44. fa[y] = x; d[y] = D; st[++top] = y;
  45. return ;
  46. }
  47. void del(int tp) {
  48. while (top > tp) {
  49. int x = st[top--];
  50. if (x < )
  51. dep[-x]--;
  52. else
  53. fa[x] = x, d[x] = ;
  54. }
  55. }
  56. } dsu;
  57.  
  58. int n, m, T;
  59. struct P {
  60. int u, v, x, y;
  61. } p[N];
  62. int ans[N];
  63.  
  64. void solve(int l, int r, const std::vector<int> &vec) {
  65. int tp = top;
  66. int mid = l + r >> ;
  67. std::vector<int> L, R;
  68. for (int i = , sz = vec.size(); i < sz; i++) {
  69. int id = vec[i];
  70. if (p[id].x <= l && r <= p[id].y) {
  71. if (!dsu.merge(p[id].u, p[id].v)) {
  72. for (int j = l; j <= r; j++)
  73. ans[j] = ;
  74. dsu.del(tp);
  75. return;
  76. }
  77. } else {
  78. if (p[id].x <= mid)
  79. L.push_back(id);
  80. if (p[id].y > mid)
  81. R.push_back(id);
  82. }
  83. }
  84. if (l == r) {
  85. dsu.del(tp);
  86. return;
  87. }
  88. solve(l, mid, L);
  89. solve(mid + , r, R);
  90. dsu.del(tp);
  91. }
  92.  
  93. int main() {
  94. IO::read(n, m, T);
  95. dsu.init(n);
  96. std::vector<int> vec;
  97. for (int i = ; i <= m; i++)
  98. IO::read(p[i].u, p[i].v, p[i].x, p[i].y), p[i].x++, vec.push_back(i);
  99. solve(, T, vec);
  100. for (int i = ; i <= T; i++)
  101. puts(ans[i] ? "No" : "Yes");
  102. return ;
  103. }

4299. Codechef FRBSUM

来补徐州的锅...其实在暑假补过这题...但是当时是把它拿来练主席树板子的...没懂原理...

若 $[1,x]$ 都能组成,那么 $[1, x + 1]$ 的区间和肯定也都能组成。证明就是数学归纳法...或者现在区间和为 $s$,那么就是倒着把 $1$、$2$、$3$、都取走就能得到其他的值了。

这样每次区间长度至少翻倍。再用主席树维护就行了...

复杂度 $O(nlog^2 n)$,徐州加了个单点修改,三 $log$ 可过...

  1.  

4424. Cf19E Fairy

一个图为二分图的充要条件就是不存在奇环。
先求出一个dfs树,然后考虑非树边对dfs树的影响。
有几种情况需要考虑。
一、不存在自环及奇环
都可以删。
二、自环
如果存在两个自环及以上,就不可能了,因为它只能删除一条边。
有一个自环,当不存在奇环的时候就只能删除这个自环,否则也没边可删了。
三、存在一个奇环
那么这个奇环上的树边及非树边都可以删。也只有这种情况能删非树边。
四、存在多个奇环
那么能删除的边就是这些奇环的树边的交集。同时,这个交集的边不能出现在偶环上,否则奇环+偶环还是会得到奇环。
那么树上差分一下得到每条边在多少个奇环上,如果在偶环上就把路径减一下,就能处理出不能在偶环上的情况。最后就判断一下每一条边的值是否为奇环的个数。

  1. #include <bits/stdc++.h>
  2.  
  3. namespace IO {
  4. void read() {}
  5. template<class T, class... T2>
  6. void read(T &x, T2 &... oth) {
  7. x = ; T f = ; char ch = getchar();
  8. while (!isdigit(ch)) { if (ch == '-') f = -; ch = getchar(); }
  9. while (isdigit(ch)) x = x * + ch - '', ch = getchar();
  10. x *= f;
  11. read(oth...);
  12. }
  13. }
  14.  
  15. const int N = 1e6 + ;
  16. struct E {
  17. int v, ne, id;
  18. } e[N << ];
  19. int head[N], tag[N], dep[N], cnt = ;
  20. bool vis[N];
  21. int self, n, m;
  22.  
  23. void add(int u, int v, int id) {
  24. e[++cnt].v = v; e[cnt].ne = head[u]; e[cnt].id = id; head[u] = cnt;
  25. e[++cnt].v = u; e[cnt].ne = head[v]; e[cnt].id = id; head[v] = cnt;
  26. }
  27.  
  28. int tol, fei;
  29.  
  30. void dfs(int u, int f) {
  31. for (int i = head[u]; i; i = e[i].ne) {
  32. if (i == (f ^ )) continue;
  33. int v = e[i].v;
  34. if (dep[v]) {
  35. if (dep[v] > dep[u]) continue;
  36. if ((dep[u] - dep[v] + ) & ) {
  37. tol++;
  38. fei = e[i].id;
  39. tag[u]++; tag[v]--;
  40. } else {
  41. tag[u]--; tag[v]++;
  42. }
  43. } else {
  44. dep[v] = dep[u] + ;
  45. dfs(v, i);
  46. tag[u] += tag[v];
  47. }
  48. }
  49. }
  50.  
  51. std::vector<int> vec;
  52.  
  53. void dfs(int u) {
  54. vis[u] = ;
  55. for (int i = head[u]; i; i = e[i].ne) {
  56. int v = e[i].v;
  57. if (vis[v]) continue;
  58. if (tag[v] == tol)
  59. vec.push_back(e[i].id);
  60. dfs(v);
  61. }
  62. }
  63.  
  64. int main() {
  65. IO::read(n, m);
  66. for (int i = ; i <= m; i++) {
  67. int u, v;
  68. IO::read(u, v);
  69. if (u == v && !self) {
  70. self = i;
  71. continue;
  72. }
  73. if (u == v) {
  74. self = -;
  75. continue;
  76. }
  77. add(u, v, i);
  78. }
  79. if (self == -) {
  80. puts("");
  81. return ;
  82. }
  83. for (int i = ; i <= n; i++) {
  84. if (!dep[i])
  85. dep[i] = , dfs(i, );
  86. }
  87. if (tol == ) {
  88. if (self) {
  89. printf("1\n%d\n", self);
  90. } else {
  91. printf("%d\n", m);
  92. for (int i = ; i <= m; i++)
  93. printf("%d%c", i, " \n"[i == m]);
  94. }
  95. return ;
  96. }
  97. if (self) {
  98. puts("");
  99. return ;
  100. }
  101. for (int i = ; i <= n; i++)
  102. if (!vis[i])
  103. dfs(i);
  104. if (tol == )
  105. vec.push_back(fei);
  106. printf("%d\n", (int)vec.size());
  107. std::sort(vec.begin(), vec.end());
  108. for (int i = ; i < vec.size(); i++)
  109. printf("%d%c", vec[i], " \n"[i + == vec.size()]);
  110. return ;
  111. }

4652. [Noi2016]循环之美

首先,一个分数 $\frac{p}{q}$ 在 $k$ 进制下是纯循环小数,就是 $\exists t$,$p \equiv p \times k^t \pmod q$,其中 $p \bot q$,那么即为 $k^t \equiv 1 \pmod q$,要存在解,就得 $k \bot q$。
所以答案就是 $\sum_{i=1}^n\sum_{j=1}^m [i\bot j][k\bot q]$
$$\sum_{i=1}^n\sum_{j=1}^m [i\bot j][k\bot j]$$
$$=\sum_{i=1}^n\sum_{j=1}^m[k\bot j]\sum_{d|(i,j)}\mu(d)$$
$$=\sum_{d=1}^{\min\{n,m\}}[d\bot k]\mu(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[j\bot k]$$
$$=\sum_{d=1}^{\min\{n,m\}}[d\bot k]\mu(d)\lfloor\frac{n}{d}\rfloor s(\lfloor\frac{m}{d}\rfloor)$$
其中 $s(n)=\sum_{i=1}^{n}[i\bot k]=\lfloor\frac{n}{k}\rfloor s(k) + s(n$ $\text{mod}$ $k)$,这个暴力预处理即可。
要求 $f(n)=\mu(n)[n\bot k]$ 的前缀和,设 $g(n)=[n\bot k]$。
$$(f\circ g)(n)$$
$$=\sum_{d\mid n}\mu(d)[d\bot k][\frac{n}{d}\bot k]$$
$$=[n\bot k]\sum_{d\mid n}\mu(d)$$
$$=[n \bot k][n=1]$$
所以 $\sum_{i=1}^n (f\circ g)(i)=1$
所以 $f$ 的前缀和 $S(n)=1-\sum_{i=2}^{n}[i\bot k]S(\lfloor\frac{n}{i}\rfloor)$
至此解决。

  1. #include <bits/stdc++.h>
  2.  
  3. const int N = 1e6, XN = N + ;
  4. int prin, prime[XN], mu[XN];
  5. int fs[XN], gs[XN];
  6.  
  7. int gcd(int a, int b) {
  8. while (b) {
  9. a %= b;
  10. std::swap(a, b);
  11. }
  12. return a;
  13. }
  14.  
  15. void init(int k) {
  16. static bool vis[XN];
  17. mu[] = ;
  18. for (int i = ; i <= N; i++) {
  19. if (!vis[i]) {
  20. prime[++prin] = i;
  21. mu[i] = -;
  22. }
  23. for (int j = ; j <= prin && i * prime[j] <= N; j++) {
  24. vis[i * prime[j]] = ;
  25. if (i % prime[j] == ) break;
  26. mu[i * prime[j]] = -mu[i];
  27. }
  28. }
  29. for (int i = ; i <= N; i++) {
  30. fs[i] = fs[i - ] + mu[i] * (gcd(i, k) == );
  31. gs[i] = gs[i - ] + (gcd(i, k) == );
  32. }
  33. }
  34.  
  35. int k, n, m;
  36.  
  37. int GS(int n) {
  38. return (n / k) * gs[k] + gs[n % k];
  39. }
  40. #define ll long long
  41. std::unordered_map<int, ll> mp;
  42.  
  43. ll solve(int n) {
  44. if (n <= N) return fs[n];
  45. if (mp.count(n)) return mp[n];
  46. ll ans = ;
  47. for (int i = , j; i <= n; i = j + ) {
  48. j = n / (n / i);
  49. ans -= 1LL * (GS(j) - GS(i - )) * solve(n / i);
  50. }
  51. return mp[n] = ans;
  52. }
  53.  
  54. signed main() {
  55. scanf("%d%d%d", &n, &m, &k);
  56. init(k);
  57. ll ans = ;
  58. for (int i = , j; i <= std::min(n, m); i = j + ) {
  59. j = std::min(n / (n / i), m / (m / i));
  60. ans += 1LL * (solve(j) - solve(i - )) * (n / i) * GS(m / i);
  61. }
  62. printf("%lld\n", ans);
  63. return ;
  64. }

4742. [Usaco2016 Dec]Team Building

考虑将所有牛的分数从大到小排序,当分数相同时第二队的牛在前面。

这样把第一队的牛看成左括号,第二队的牛看成右括号,那就相当于要选出一个合法的括号序列。

$dp[i][j][k]$ 表示前 $i$ 头牛选了 $j$ 个第一队的,选了 $k$ 个第二队的方案数。

转移过程中保证 $j$ 大于 $k$ 即可。

4816. [Sdoi2017]数字表格

$$\prod_{i=1}^{n}\prod_{j=1}^{m}f(\gcd(i,j))$$
$$=\prod_{d=1}^{\min\{n,m\}}f(d)^{\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}[(i,j)=1]}$$
$$=\prod_{d=1}^{\min\{n,m\}}f(d)^{\sum_{d'}\mu(d')\lfloor\frac{n}{dd'}\rfloor\lfloor\frac{m}{dd'}\rfloor}$$
$$=\prod_{T}(\prod_{d|T}f(d)^{\mu(\frac{T}{d})})^{\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}$$
求出 $g(n) =\prod_{d|n}f(d)^{\mu(\frac{n}{d})}$ 的前缀积即可。

  1. #include <bits/stdc++.h>
  2.  
  3. const int MOD = 1e9 + ;
  4.  
  5. int qp(int a, int b = MOD - ) {
  6. int ans = ;
  7. while (b) {
  8. if (b & ) ans = 1LL * ans * a % MOD;
  9. b >>= ;
  10. a = 1LL * a * a % MOD;
  11. }
  12. return ans;
  13. }
  14.  
  15. const int N = 1e6;
  16. int prime[N + ], prin, mu[N + ], g[N + ], f[N + ], fi[N + ];
  17. bool vis[N + ];
  18.  
  19. inline int add(int x, int y) {
  20. return x + y >= MOD ? x + y - MOD : x + y;
  21. }
  22.  
  23. void init() {
  24. mu[] = ;
  25. f[] = ; f[] = ;
  26. fi[] = ;
  27. g[] = g[] = ;
  28. for (int i = ; i <= N; i++) {
  29. g[i] = ;
  30. f[i] = add(f[i - ], f[i - ]);
  31. fi[i] = qp(f[i]);
  32. if (!vis[i]) {
  33. prime[++prin] = i;
  34. mu[i] = -;
  35. }
  36. for (int j = ; j <= prin && i * prime[j] <= N; j++) {
  37. vis[i * prime[j]] = ;
  38. if (i % prime[j] == ) break;
  39. mu[i * prime[j]] = -mu[i];
  40. }
  41. }
  42. for (int i = ; i <= N; i++) if (mu[i])
  43. for (int j = ; 1LL * i * j <= N; j++)
  44. g[i * j] = 1LL * g[i * j] * (mu[i] == ? f[j] : fi[j]) % MOD;
  45. for (int i = ; i <= N; i++)
  46. g[i] = 1LL * g[i] * g[i - ] % MOD;
  47. }
  48.  
  49. int main() {
  50. init();
  51. int T;
  52. scanf("%d", &T);
  53. while (T--) {
  54. int n, m;
  55. scanf("%d%d", &n, &m);
  56. if (n > m) std::swap(n, m);
  57. int ans = ;
  58. for (int i = , j; i <= n; i = j + ) {
  59. j = std::min(n / (n / i), m / (m / i));
  60. ans = 1LL * ans * qp(1LL * g[j] * qp(g[i - ]) % MOD, 1LL * (n / i) * (m / i) % (MOD - )) % MOD;
  61. }
  62. printf("%d\n", ans);
  63. }
  64. return ;
  65. }

BZOJ练习记的更多相关文章

  1. BZOJ开荒记

    2019/4/16 1:04 使用Yinku2017提交了第一发,当然是A+B Problem. 看一下排行榜,算一下区域赛还有180多天吧?先用30天过50道题(含A+B Problem)怎么样?

  2. BZOJ.4738.[清华集训2016]汽水(点分治 分数规划)

    BZOJ UOJ 记\(val_i\)是每条边的边权,\(s\)是边权和,\(t\)是经过边数,\(k\)是给定的\(k\). 在点分治的时候二分答案\(x\),设\(|\frac st-k|=x\) ...

  3. 【BZOJ】【3157】&【BZOJ】【3516】国王奇遇记

    数论 题解:http://www.cnblogs.com/zhuohan123/p/3726933.html copy一下推导过程: 令$$S_i=\sum_{k=1}^{n}k^im^k$$ 我们有 ...

  4. [BZOJ 3157] 国王奇遇记

    Link: BZOJ 3157 传送门 Solution: 题意:求解$\sum_{i=1}^n m^i \cdot {i^m}$ $O(m^2)$做法: 定义一个函数$f[i]$,$f[i]=\su ...

  5. 3157: 国王奇遇记 & 3516: 国王奇遇记加强版 - BZOJ

    果然我数学不行啊,题解君: http://www.cnblogs.com/zhuohan123/p/3726933.html const h=; var fac,facinv,powm,s:..]of ...

  6. BZOJ 3157: 国王奇遇记 (数学)

    题面:BZOJ3157 一句话题意: 求: \[ \sum_{i=1}^ni^m\ \times m^i\ (mod\ 1e9+7)\ \ (n \leq 1e9,m\leq200)\] 题解 令 \ ...

  7. BZOJ 3516 国王奇遇记加强版(乱推)

    题意 求\(\sum_{k=1}^{n}k^mm^k (n\leq1e9,m\leq1e3)\) 思路 在<>中有一个方法用来求和,称为摄动法. 我们考虑用摄动法来求这个和式,看能不能得到 ...

  8. bzoj 3157 && bzoj 3516 国王奇遇记——推式子

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3157 https://www.lydsy.com/JudgeOnline/problem.p ...

  9. bzoj 3157 & bzoj 3516 国王奇遇记 —— 推式子

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3157 https://www.lydsy.com/JudgeOnline/problem.p ...

随机推荐

  1. C++ TCP客户端网络消息发送接收同步实现

    废话不多说, 直入主题, 我们在写客户单的时候希望在哪里发消息出去,然后在哪里返回消息(同步), 然后继续往下运行-, 而不是在这里发送了一个消息给服务端, 在另一个地方接受消息(异步) , 也不知道 ...

  2. 软件工程实践2019——idea表述及组队

    时间:2019-10-08 随堂 欢迎每个有想法的同学都积极参与idea表述,用心呈现你的心中所想.你心中热爱的,希望在软工实践项目中完成的项目作品.每个愿意表达idea的同学,都有一分钟时间来呈现作 ...

  3. 解决office365无法登录以及同步的问题

    解决office365无法登录以及同步的问题 You better need to test them one by one. You better need to test them one by ...

  4. Mysql中使用JDBC流式查询避免数据量过大导致OOM

    一.前言 java 中MySQL JDBC 封装了流式查询操作,通过设置几个参数,就可以避免一次返回数据过大导致 OOM. 二.如何使用 2.1 之前查询 public void selectData ...

  5. ASP.NET Core快速入门(第1章:介绍与引入)--学习笔记

    课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务1:课程介绍 1.介绍与引入 2.配置管理 3.依赖注入 4.ASP.NE ...

  6. Grafana的Docker部署方式

    docker run -d -p : --name=grafana544 -v D:/grafana/grafana-/data:/var/lib/grafana -v D:/grafana/graf ...

  7. Winform 窗体皮肤美化_IrisSkin

    1 先把IrisSkin2.dll文件添加到当前项目引用(解决方案资源管理器->当前项目->引用->右键->添加引用,找到IrisSkin2.dll文件.....之后就不用我说 ...

  8. UWP使用Microsoft.Data.Sqlite的记录

    我在UWP中使用SQLite数据库时,并没有使用网上的SQLite for Universal App Platform方案,而使用了Microsoft和SQLite社区一起维护的Microsoft. ...

  9. 模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  10. Git上传到码云及其常见问题详解

    1.git init 初始化 2.git  remote origin add https://gitee.com/su_yong_qing/SyqSystem.git 这里注意把链接替换为自己的仓库 ...