[关闭]
@ljt12138 2017-04-09T23:43:31.000000Z 字数 16496 阅读 866

bzoj刷题记录:4.5-4.9


bzoj1027: [JSOI2007]合金

神题...

首先第三维可以忽略。

  1. 若干个原材料能合成一种合金,当且仅当其(x,y)在这几种原材料的多边形内。因此变成一个问题就是包住目标节点的最小节点数多边形。
  2. 然后居然可以floyd...(i,j)有边,如果所有目标节点都在(i,j)连线的右侧。然后多边形就变成一个环了。最小环就是答案。
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. struct p {
  4. double x, y;
  5. p operator - (const p &a)
  6. { return (p) {x-a.x, y-a.y}; }
  7. double operator * (const p &a)
  8. { return x*a.y-y*a.x; }
  9. } mat[505], tar[505];
  10. int m, n;
  11. double tmp, eps = 1e-10;
  12. double dot(const p &a, const p &b)
  13. {
  14. return a.x*b.x + a.y*b.y;
  15. }
  16. int sgn(double x)
  17. {
  18. if (abs(x) <= eps) return 0;
  19. return x > 0 ? 1 : -1;
  20. }
  21. bool on_side(int i, int j)
  22. {
  23. for (int k = 1; k <= n; k++)
  24. if ((tar[k]-mat[i])*(mat[j]-mat[i]) > eps ||
  25. (abs((tar[k]-mat[i])*(mat[j]-mat[i])) <= eps && dot(mat[i]-tar[k], mat[j]-tar[k]) > eps)) return 0;
  26. // cout << i << " " << j << " " << gt << " " << ls << " " << endl;
  27. return 1;
  28. }
  29. int g[505][505];
  30. int main()
  31. {
  32. scanf("%d%d", &m, &n);
  33. for (int i = 1; i <= m; i++)
  34. scanf("%lf%lf%lf", &mat[i].x, &mat[i].y, &tmp);
  35. for (int i = 1; i <= n; i++)
  36. scanf("%lf%lf%lf", &tar[i].x, &tar[i].y, &tmp);
  37. memset(g, 127/3, sizeof g);
  38. for (int i = 1; i <= m; i++)
  39. for (int j = 1; j <= m; j++)
  40. if (on_side(i, j))
  41. g[i][j] = 1;
  42. for (int k = 1; k <= m; k++)
  43. for (int i = 1; i <= m; i++)
  44. for (int j = 1; j <= m; j++)
  45. g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
  46. int ans = 233333333;
  47. for (int i = 1; i <= m; i++) ans = min(ans, g[i][i]);
  48. if (ans >= 23333333) cout << -1 << endl;
  49. else cout << ans << endl;
  50. return 0;
  51. }

bzoj1026: [SCOI2009]windy数

第一道数位dp233...果然神烦,调了一个小时左右吧(主要还是递推关系找错)
就是用dp[i][j][k]表示前i位中,最后一位填j,能否随便填/为前导零,然后一堆判断即可。
small trick:用把逻辑判断深入中,防止逻辑出错。
偷了个懒用了ctrl+c ctrl+v
另外吐槽黄学长代码随手hack...

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. long long dp[20][10][4]; // 前i位,最后一位是j,能不能随便填/前导0
  4. long long A, B;
  5. int SA[20], SB[20];
  6. int lA = 0, lB = 0;
  7. int main()
  8. {
  9. scanf("%lld%lld", &A, &B); A--;
  10. for (long long i = A; i; i /= 10) SA[++lA] = i%10;
  11. for (long long i = B; i; i /= 10) SB[++lB] = i%10;
  12. /* calc ans ls B*/
  13. for (int i = 1; i < SB[lB]; i++) dp[lB][i][1] = 1;
  14. dp[lB][SB[lB]][0] = 1, dp[lB][0][2] = 1;
  15. for (int i = lB-1; i >= 1; i--) {
  16. for (int j = 0; j < 10; j++) {
  17. for (int k = 0; k <= 2; k++)
  18. for (int l = 0; l < 10; l++)
  19. for (int p = 0; p <= 2; p++)
  20. dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*
  21. ((k==0&&p==0&&j==SB[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SB[i])))||(k==2&&j==0&&p==2));
  22. }
  23. }
  24. long long ans1 = 0, ans2 = 0;
  25. for (int i = 0; i < 10; i++) ans1 += dp[1][i][0]+dp[1][i][1];//+dp[1][i][2];
  26. memset(dp, 0, sizeof dp);
  27. /* now ls A*/
  28. for (int i = 1; i < SA[lA]; i++) dp[lA][i][1] = 1;
  29. dp[lA][SA[lA]][0] = 1, dp[lA][0][2] = 1;
  30. for (int i = lA-1; i >= 1; i--) {
  31. for (int j = 0; j < 10; j++) {
  32. for (int k = 0; k <= 2; k++)
  33. for (int l = 0; l < 10; l++)
  34. for (int p = 0; p <= 2; p++)
  35. dp[i][j][k] += dp[i+1][l][p]*(p==2||abs(j-l) >= 2)*
  36. ((k==0&&p==0&&j==SA[i])||(k==1&&(p==1||(p==2&&j)||(p==0&&j<SA[i])))||(k==2&&j==0&&p==2));
  37. }
  38. }
  39. for (int i = 0; i < 10; i++) ans2 += dp[1][i][0]+dp[1][i][1];
  40. cout << ans1-ans2 << endl;
  41. return 0;
  42. }

bzoj1069: [SCOI2007]最大土地面积

显然(表示蒟蒻看不出哪里显然)四边形的两个点是凸包上一对对踵点,所以旋转卡壳找对踵点然后枚举另外左右两个点就好了。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. struct vec {
  4. double x, y;
  5. double len_pow() const
  6. { return x*x+y*y; }
  7. friend vec operator - (const vec &a, const vec &b)
  8. { return (vec) {a.x-b.x, a.y-b.y}; }
  9. friend double operator * (const vec &a, const vec &b)
  10. { return a.x*b.y-a.y*b.x; }
  11. friend bool operator == (const vec &a, const vec &b)
  12. { return b.x == a.x && b.y == a.y; }
  13. } pts[2005];
  14. int n;
  15. vec lft;
  16. double eps = 1e-7;
  17. int sgn(double a)
  18. { return abs(a) <= eps ? 0 : (a>eps?1:-1); }
  19. bool cmp(const vec &a, const vec &b)
  20. {
  21. if (a == lft || b == lft) return a == lft;
  22. return sgn((b-lft)*(a-lft)) == 0 ? a.len_pow() < b.len_pow() : sgn((b-lft)*(a-lft)) > 0;
  23. }
  24. stack<vec> stk;
  25. vec pl[2005];
  26. int tp = 0;
  27. void graham()
  28. {
  29. lft.x = 1234567;
  30. for (int i = 1; i <= n; i++)
  31. if (pts[i].x < lft.x) lft = pts[i];
  32. sort(pts+1, pts+n+1, cmp);
  33. for (int i = 1; i <= n; i++) {
  34. if (stk.empty()) stk.push(pts[i]);
  35. else {
  36. while (stk.size() >= 2) {
  37. vec tp = stk.top(); stk.pop();
  38. vec t2 = stk.top(); stk.pop();
  39. if ((t2-tp)*(pts[i]-tp) > 0) {stk.push(t2), stk.push(tp); break;}
  40. else stk.push(t2);
  41. }
  42. stk.push(pts[i]);
  43. }
  44. }
  45. stk.push(lft);
  46. tp = stk.size();
  47. for (int i = tp-1; i >= 0; i--)
  48. pl[i] = stk.top(), stk.pop();
  49. double ans = 0;
  50. for (int i = 0, j = 1; i < tp; i++) {
  51. double l = 0, r = 0;
  52. if (j == i) j = (j+1)%tp;
  53. while((pl[(j+1)%tp]-pl[i]).len_pow() >= (pl[j]-pl[i]).len_pow())
  54. j = (j+1)%tp;
  55. for (int k = 0; k < tp; k++)
  56. if (sgn((pl[k]-pl[i])*(pl[j]-pl[i])) <= 0) l = max(l, abs((pl[k]-pl[i])*(pl[j]-pl[i]))/2);
  57. else r = max(r, abs((pl[k]-pl[i])*(pl[j]-pl[i]))/2);
  58. ans = max(ans, l+r);
  59. }
  60. printf("%.3f", ans);
  61. }
  62. int main()
  63. {
  64. scanf("%d", &n);
  65. for (int i = 1; i <= n; i++)
  66. scanf("%lf %lf", &pts[i].x, &pts[i].y);
  67. graham();
  68. return 0;
  69. }

bzoj1031: [JSOI2007]字符加密Cipher

输入S,把SS丢进后缀数组排序,然后扫一遍就可以了。
然而后缀数组写一遍就要看一遍模板...
一会再练习一下二分+hash大法...感觉做这个题应该很轻松

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 200005;
  4. struct SA {
  5. struct p {
  6. int k[2], id;
  7. p(){}
  8. p(int i, int a, int b) { id = i, k[0] = a, k[1] = b; }
  9. } RE[MAXN], RT[MAXN];
  10. int C[MAXN], SA[MAXN], height[MAXN], rank[MAXN], str[MAXN], n;
  11. void radix_sort()
  12. {
  13. for (int y = 1; y >= 0; y--) {
  14. memset(C, 0, sizeof C);
  15. for (int i = 1; i <= n; i++) C[RE[i].k[y]]++;
  16. for (int i = 1; i <= 200000; i++) C[i] += C[i-1];
  17. for (int i = n; i >= 1; i--) RT[C[RE[i].k[y]]--] = RE[i];
  18. for (int i = 1; i <= n; i++) RE[i] = RT[i];
  19. }
  20. for (int i = 1; i <= n; i++) {
  21. rank[RE[i].id] = rank[RE[i-1].id];
  22. if (RE[i].k[0] != RE[i-1].k[0] || RE[i].k[1] != RE[i-1].k[1])
  23. rank[RE[i].id]++;
  24. }
  25. }
  26. void calc_sa()
  27. {
  28. for (int i = 1; i <= n; i++) RE[i] = p(i, str[i], 0);
  29. radix_sort();
  30. for (int k = 1; k < n; k <<= 1) {
  31. for (int i = 1; i <= n; i++) RE[i] = p(i, rank[i], i+k<=n?rank[i+k]:0);
  32. radix_sort();
  33. }
  34. for (int i = 1; i <= n; i++)
  35. SA[rank[i]] = i;
  36. }
  37. } SA;
  38. char str[MAXN];
  39. int main()
  40. {
  41. scanf("%s", str+1);
  42. int n = strlen(str+1); SA.n = n*2;
  43. for (int i = 1; i <= n; i++) SA.str[i] = SA.str[i+n] = str[i];
  44. SA.calc_sa();
  45. for (int i = 1; i <= n*2; i++) {
  46. if (SA.SA[i] <= n) {
  47. printf("%c", SA.str[SA.SA[i]+n-1]);
  48. }
  49. }
  50. return 0;
  51. }

bzoj1040: [ZJOI2008]骑士

这就是所谓环套树上dp吗?
求环套树上最大独立集。
由于每棵树至多一个环,只需要从(u,v)拆开这个环,做两次dp。一次不选u,一次不选v,然后取最大值。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1000005;
  4. int x[MAXN];
  5. int n;
  6. struct node {
  7. int to, next;
  8. } edge[MAXN*2];
  9. int head[MAXN], top = 0;
  10. void push(int i, int j)
  11. { ++top, edge[top] = (node) { j, head[i]}, head[i] = top; }
  12. struct tree {
  13. struct node {
  14. int to, next;
  15. } edge[MAXN];
  16. int head[MAXN], top, vis[MAXN], rd[MAXN], v[MAXN], vt;
  17. long long dp[MAXN], dpban[MAXN];
  18. void clear()
  19. { top = vt = 0; memset(head, 0, sizeof head); memset(vis, -1, sizeof vis);
  20. memset(rd, 0, sizeof rd); memset(v, 0, sizeof v); }
  21. void push(int i, int j)
  22. {
  23. //cout << i<<"-->" << j << endl;
  24. ++top, edge[top] = (node) { j, head[i]}, head[i] = top, rd[j]++;
  25. if (vis[i] == -1) v[++vt] = i, vis[i] = 0;
  26. if (vis[j] == -1) v[++vt] = j, vis[j] = 0;
  27. }
  28. long long dfs(int nd)
  29. {
  30. // cout << "dfs " << nd << endl;
  31. if (dp[nd] != -1) return dp[nd];
  32. if (vis[nd]) return dfs_ban(nd);
  33. // --- choose nd ---
  34. dp[nd] = x[nd];
  35. for (int i = head[nd]; i; i = edge[i].next)
  36. dp[nd] += dfs_ban(edge[i].to);
  37. // --- not choose nd ---
  38. long long ans = 0;
  39. for (int i = head[nd]; i; i = edge[i].next)
  40. ans += dfs(edge[i].to);
  41. return dp[nd] = max(dp[nd], ans);
  42. }
  43. long long dfs_ban(int nd)
  44. {
  45. //cout << "dfs_ban " << nd << endl;
  46. if (dpban[nd] != -1) return dpban[nd];
  47. dpban[nd] = 0;
  48. for (int i = head[nd]; i; i = edge[i].next)
  49. dpban[nd] += dfs(edge[i].to);
  50. return dpban[nd];
  51. }
  52. long long result()
  53. {
  54. memset(dp, -1, sizeof dp), memset(dpban, -1, sizeof dpban);
  55. int rt = 0;
  56. for (int i = 1; i <= vt; i++)
  57. if (rd[v[i]] == 0) {
  58. rt = v[i];
  59. break;
  60. }
  61. return dfs(rt);
  62. }
  63. } T;
  64. int vis[MAXN];
  65. int u, v; // 拆环点
  66. void dfs(int i, int f = 0)
  67. {
  68. vis[i] = 1;
  69. for (int k = head[i]; k; k = edge[k].next) {
  70. int to = edge[k].to;
  71. if (f == to);
  72. else if (!vis[to]) T.push(i, to), dfs(to, i);
  73. else u = i, v = to;
  74. }
  75. }
  76. long long work(int i)
  77. {
  78. T.clear();
  79. dfs(i);
  80. // cout << u << " " << v << endl;
  81. long long ans = 0;
  82. T.vis[u] = 0, T.vis[v] = 1;
  83. ans = max(ans, T.result());
  84. T.vis[v] = 0, T.vis[u] = 1;
  85. ans = max(ans, T.result());
  86. return ans;
  87. }
  88. int main()
  89. {
  90. scanf("%d", &n);
  91. for (int i = 1; i <= n; i++) {
  92. int tar;
  93. scanf("%d%d", &x[i], &tar);
  94. push(i, tar), push(tar, i);
  95. }
  96. memset(vis, 0, sizeof vis);
  97. long long ans = 0;
  98. for (int i = 1; i <= n; i++)
  99. if (!vis[i])
  100. ans += work(i);
  101. cout << ans << endl;
  102. return 0;
  103. }

bzoj2234: 营业额统计

set水过...
鉴于那个年代不能stl,再写一个splay..
其实那个年代也没有splay

set:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. set<int> table;
  4. int n;
  5. int main()
  6. {
  7. scanf("%d", &n);
  8. int cnt = 0;
  9. for (int i = 1; i <= n; i++) {
  10. int a; if (scanf("%d", &a)==EOF) a = 0;
  11. set<int>::iterator k = table.lower_bound(a), t = k;
  12. if (k != table.begin()) k--;
  13. int ans = INT_MAX;
  14. if (i == 1) ans = a;
  15. else {
  16. if (t != table.end()) ans = min(ans, abs(*t-a));//, printf("--%d--\n", *t);
  17. if (k != table.end()) ans = min(ans, abs(*k-a));//, printf("--%d--\n", *k);
  18. }
  19. cnt += ans;
  20. table.insert(a);
  21. }
  22. cout << cnt << endl;
  23. return 0;
  24. }

splay:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 40005;
  4. int dat[MAXN], chl[MAXN][2], fa[MAXN], top = 0, root = 0;
  5. int new_node(int ke, int f)
  6. { return dat[++top] = ke, fa[top] = f, top; }
  7. void zig(int nd)
  8. {
  9. int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
  10. int son = chl[nd][tp^1];
  11. if (son) fa[son] = p; fa[p] = nd, fa[nd] = g;
  12. if (g) chl[g][tg] = nd; else root = nd;
  13. chl[nd][tp^1] = p, chl[p][tp] = son;
  14. }
  15. void splay(int nd)
  16. {
  17. while (fa[nd]) {
  18. int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
  19. if (!g) {zig(nd); break; }
  20. if (tp == tg) zig(p), zig(nd);
  21. else zig(nd), zig(nd);
  22. }
  23. }
  24. void push_ele(int ke)
  25. {
  26. if (!root) { root = new_node(ke, 0); return; }
  27. int nd = root, p = fa[nd];
  28. while (nd) p = nd, nd = chl[nd][dat[nd]<=ke];
  29. chl[p][dat[p] <= ke] = new_node(ke, p);
  30. splay(chl[p][dat[p]<=ke]);
  31. }
  32. int ans_of(int num)
  33. {
  34. if (!root) return num;
  35. int ans = INT_MAX;
  36. int nd = root;
  37. while (nd) {
  38. ans = min(ans, abs(dat[nd]-num));
  39. nd = chl[nd][dat[nd]<=num];
  40. }
  41. return ans;
  42. }
  43. int main()
  44. {
  45. int n, a;
  46. memset(chl, 0, sizeof chl);
  47. memset(fa, 0, sizeof fa);
  48. scanf("%d", &n);
  49. int sum = 0;
  50. for (int i = 1; i <= n; i++) {
  51. if (scanf("%d", &a) == EOF) a = 0;
  52. sum += ans_of(a), push_ele(a);
  53. }
  54. cout << sum << endl;
  55. return 0;
  56. }

bzoj1019: [SHOI2008]汉诺塔

手玩一会发现方法是唯一的。
凡汉诺塔就考虑最大盘,枚举最大盘两种行进模式,然后递归判断是否可行。那个优先级限制事实上只作用在一个盘子的时候。
最后带个记忆化即可。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. long long dp[35][4][4];
  4. pair<int,int> pr[7];
  5. int n;
  6. long long dfs(int n, int s, int t)
  7. {
  8. if (dp[n][s][t] != -1) return dp[n][s][t];
  9. if (n == 1) {
  10. for (int i = 1; i <= 6; i++) {
  11. if (pr[i].first == s && pr[i].second != t) {dp[n][s][t] = -1234567; break;}
  12. if (pr[i].first == s && pr[i].second == t) {dp[n][s][t] = 1; break;}
  13. }
  14. } else {
  15. if (dfs(n-1, s, t) > 0 && dfs(n-1, t, s) > 0) dp[n][s][t] = 2+dfs(n-1, s, t)*2+dfs(n-1, t, s);
  16. else if (dfs(n-1, s, 6-s-t) > 0 && dfs(n-1, 6-s-t, t) > 0) dp[n][s][t] = dfs(n-1, s, 6-s-t) + dfs(n-1, 6-s-t, t) + 1;
  17. else dp[n][s][t] = -1234567;
  18. }
  19. // printf("%d,%d,%d ==> %lld\n", n, s, t, dp[n][s][t]);
  20. return dp[n][s][t];
  21. }
  22. int main()
  23. {
  24. memset(dp, -1, sizeof dp);
  25. scanf("%d", &n);
  26. for (int i = 1; i <= 6; i++) {
  27. char s[2];
  28. scanf("%s", s);
  29. pr[i] = make_pair(s[0]-'A'+1, s[1]-'A'+1);
  30. }
  31. if (dfs(n, 1, 3)>0) cout << dfs(n, 1, 3) << endl;
  32. else cout << dfs(n, 1, 2) << endl;
  33. return 0;
  34. }

bzoj1022: [SHOI2008]小约翰的游戏John

和NIM很像啊,然而找不到太多思路,然后就用通用策略打表找规律,可以发现很强的结论:只需要特判非0即1的局面,剩下的和普通NIM一样(SG&XOR)。

  1. #include <cstdio>
  2. using namespace std;
  3. int main()
  4. {
  5. int T;
  6. scanf("%d", &T);
  7. while (T--) {
  8. int n; scanf("%d", &n);
  9. int ans = 0, cnt = 0, c2 = 0;
  10. for (int i = 1; i <= n; i++) {
  11. int a; scanf("%d", &a);
  12. ans ^= a;
  13. if (a == 1) cnt++;
  14. if (a == 0) c2++;
  15. }
  16. if (cnt+c2 == n) {
  17. if (cnt&1) puts("Brother");
  18. else puts("John");
  19. } else {
  20. if (ans == 0) puts("Brother");
  21. else puts("John");
  22. }
  23. }
  24. return 0;
  25. }

bzoj1188: [HNOI2007]分裂游戏

题不是今天写的,但还是mark一下。
分裂之后两个石子就独立了,所以:

sg(n)=mex{sg(i)sg(j) | i>n,ji}

n很小,暴力算就可以了。
至于计数题,仍然大暴力,枚举分裂位置求新局面的sg异或和即可。
设原局面异或和为ans,新位置i(j,k),相当于去掉i,加上j,k,由于任何一个元素都是异或的幂等元,去掉等同于加上,新的异或和为

anssg(i)sg(j)sg(k)

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int a[30], sg[30];
  4. int n;
  5. int basket[100];
  6. int main()
  7. {
  8. int t; scanf("%d", &t);
  9. while (t--) {
  10. scanf("%d", &n);
  11. for (int i = 1; i <= n; i++)
  12. scanf("%d", &a[i]);
  13. sg[n] = 0;
  14. for (int i = n-1; i >= 1; i--) {
  15. memset(basket, 0, sizeof basket);
  16. for (int j = i+1; j <= n; j++)
  17. for (int k = j; k <= n; k++)
  18. basket[sg[j]^sg[k]] = 1;
  19. for (int j = 0; ; j++) if (basket[j] == 0) { sg[i] = j; break; }
  20. }
  21. int ans = 0;
  22. for (int i = 1; i <= n; i++)
  23. if (a[i]&1)
  24. ans ^= sg[i];
  25. if (ans == 0) {
  26. printf("-1 -1 -1\n0\n");
  27. } else {
  28. int a1, b, c, cnt = 0; a1 = b = c = -1;
  29. for (int i = 1; i <= n; i++)
  30. for (int j = i+1; j <= n; j++)
  31. for (int k = j; k <= n; k++)
  32. if (((ans^sg[i]^sg[j]^sg[k]) == 0) ){
  33. if (a1+b+c < 0) a1 = i, b = j, c = k;
  34. cnt++;
  35. }
  36. printf("%d %d %d\n%d\n", a1-1, b-1, c-1, cnt);
  37. }
  38. }
  39. return 0;
  40. }

bzoj2728: [HNOI2012]与非

神题......磕了一上午无果,后来看了大爷题解。
首先发现{=nand}构成一个联结词完备集,因为:

  1. ¬a=aa
  2. ab=¬(ab)
  3. ab=¬(¬a¬b)

然后就用奇奇怪怪的方法搞了一个每个位置都仅出现在一个数的线性基出来,然后贪心...

大爷题解传送门

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. long long bas[70];
  4. int n, kp, k; long long L, R;
  5. long long a[1010], upp;
  6. int v[70];
  7. long long query(long long up)
  8. {
  9. if (up == -1) return -1;
  10. long long now = 0, ans = 0;
  11. for (int i = 1; i <= k; i++) {
  12. if ((now|bas[i]) <= up)
  13. now |= bas[i], ans += (1ll<<(k-i));
  14. }
  15. return ans;
  16. }
  17. int main()
  18. {
  19. memset(v, 0, sizeof v);
  20. scanf("%d%d%lld%lld", &n, &kp, &L, &R);
  21. for (int i = 1; i <= n; i++)
  22. scanf("%lld", &a[i]);
  23. upp = (1ll<<kp)-1;
  24. for (int j = kp-1; j >= 0; j--)
  25. if (!v[j]) {
  26. long long now = upp;
  27. for (int i = 1; i <= n; i++)
  28. if (!(a[i]&(1ll<<j)))
  29. now &= ~a[i]&upp;
  30. else
  31. now &= a[i];
  32. bas[++k] = now;
  33. for (int i = 0; i <= j; i++)
  34. if (now&(1ll<<i))
  35. v[i] = 1;
  36. }
  37. cout << query(R)-query(L-1) << endl;
  38. return 0;
  39. }

上午那个线性基太伤人...于是下午打算复习tarjan。
其实是重新学...
以前很喜欢做缩点不过只会ko..算法,学了tarjan感觉打开了新世界大门。才知道那个stack存的是根到这个点的路径...

bzoj2730: [HNOI2012]矿场搭建

貌似有大爷用点双做?
首先用tarjan算出割点集S,然后判断GG(S)的每个联通块。如果和两个割点相连则不用管,如果仅和一个割点连着要建一个。最后乘一乘就好。
特判没有割点要建两个。
初始化坑人...

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1050;
  4. int m;
  5. int arr[MAXN], tp = 0;
  6. struct graph {
  7. struct node {
  8. int to, next;
  9. } edge[MAXN];
  10. int head[MAXN], n, top, low[MAXN], dfn[MAXN], vis[MAXN], deg[MAXN], dft;
  11. int cut[MAXN], gp[MAXN], gp_top, lk[MAXN][MAXN], cut_cnt;
  12. void clear()
  13. { memset(arr, 0, sizeof arr); memset(head,0,sizeof head); memset(vis, 0, sizeof vis);
  14. memset(cut, 0, sizeof cut); n = top = dft = gp_top = cut_cnt = 0;
  15. memset(deg, 0, sizeof deg); memset(gp, 0, sizeof gp);
  16. memset(dfn, 0, sizeof dfn); memset(lk, 0, sizeof lk); }
  17. void push(int i, int j)
  18. { ++top, edge[top] = (node) {j, head[i]}, head[i] = top; n = max(n, max(i,j)); }
  19. void tarjan(int nd, int f);
  20. void work(int);
  21. void dfs(int nd, int &siz, int &lkt);
  22. } g;
  23. void graph::tarjan(int nd, int f)
  24. {
  25. vis[nd] = 1, low[nd] = dfn[nd] = ++dft;
  26. for (int i = head[nd]; i; i = edge[i].next) {
  27. int to = edge[i].to;
  28. if (!vis[to]) tarjan(to, nd), low[nd] = min(low[nd], low[to]), deg[nd]++;
  29. else if (to != f) low[nd] = min(low[nd], dfn[to]);
  30. if (dfn[nd] <= low[to]) {if (!f) cut[nd] = deg[nd]>=2; else cut[nd] = 1; cut_cnt += cut[nd];}
  31. }
  32. }
  33. void graph::dfs(int nd, int &siz, int &lkt)
  34. {
  35. gp[nd] = gp_top, siz++;
  36. // cerr << "Group " << gp_top << " ---> " << nd << endl;
  37. for (int i = head[nd]; i; i = edge[i].next) {
  38. int to = edge[i].to;
  39. if (!gp[to] && !cut[to]) dfs(to, siz, lkt);
  40. else if (cut[to] && lk[gp_top][to] == 0) lk[gp_top][to] = 1, lkt++;
  41. }
  42. }
  43. void graph::work(int c)
  44. {
  45. for (int i = 1; i <= n; i++)
  46. if (!dfn[i])
  47. tarjan(i, 0);
  48. if (cut_cnt == 0) {
  49. printf("Case %d: %d %lld\n", c, 2, (long long)n*(n-1)/2);
  50. return;
  51. }
  52. for (int i = 1; i <= n; i++) {
  53. if (!cut[i] && !gp[i]) {
  54. int siz = 0, lkt = 0;
  55. gp_top++, dfs(i, siz, lkt);
  56. if (lkt == 1) arr[++tp] = siz;
  57. }
  58. }
  59. long long ans = 1;
  60. for (int i = 1; i <= tp; i++) ans *= arr[i];
  61. printf("Case %d: %d %lld\n", c, tp, ans);
  62. }
  63. int main()
  64. {
  65. int cas = 0;
  66. while (scanf("%d", &m)) {
  67. g.clear(); tp = 0;
  68. if (m == 0) break;
  69. ++cas;
  70. for (int i = 1; i <= m; i++) {
  71. int u, v; scanf("%d %d", &u, &v);
  72. g.push(u, v), g.push(v, u);
  73. }
  74. g.work(cas);
  75. }
  76. return 0;
  77. }

bzoj1051: [HAOI2006]受欢迎的牛

做过好几遍了...
练模板首选2333

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 10005, MAXM = 100005;
  4. int n, m;
  5. struct graph {
  6. struct node {
  7. int to, next;
  8. } edge[MAXM];
  9. int head[MAXN], top, d[MAXN];
  10. graph(){top = 0; memset(head, 0, sizeof head),memset(d, 0, sizeof d); }
  11. void push(int i, int j)
  12. { d[i]++, ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }
  13. } g, g1;
  14. int dfn[MAXN], low[MAXN], stk[MAXN], top = 0, vis[MAXN], dft = 0;
  15. int gp[MAXN], gtop = 0, gsiz[MAXN];
  16. set<pair<int,int> > hst;
  17. void tarjan(int nd)
  18. {
  19. dfn[nd] = low[nd] = ++dft, vis[nd] = 1, stk[++top] = nd;
  20. for (int i = g.head[nd]; i; i = g.edge[i].next) {
  21. int to = g.edge[i].to;
  22. if (!dfn[to]) tarjan(to), low[nd] = min(low[nd], low[to]);
  23. else if (vis[to]) low[nd] = min(low[nd], dfn[to]);
  24. }
  25. if (low[nd] == dfn[nd]) {
  26. gtop++;
  27. int now;
  28. do {
  29. now = stk[top--];
  30. // printf("%d ", now);
  31. gp[now] = gtop, gsiz[gtop]++;
  32. vis[now] = 0;
  33. } while(now != nd); //puts("");
  34. }
  35. }
  36. void work()
  37. {
  38. int cnt = 0, ans = 0;
  39. for (int i = 1; i <= gtop; i++) if (g1.d[i] == 0) cnt++, ans += gsiz[i];
  40. if (cnt != 1) puts("0");
  41. else printf("%d\n", ans);
  42. }
  43. int main()
  44. {
  45. scanf("%d%d", &n, &m);
  46. for (int i = 1; i <= m; i++) {
  47. int u, v; scanf("%d%d", &u, &v);
  48. g.push(u, v);
  49. }
  50. for (int i = 1; i <= n; i++)
  51. if (!dfn[i])
  52. tarjan(i);
  53. for (int i = 1; i <= n; i++) {
  54. for (int j = g.head[i]; j; j = g.edge[j].next) {
  55. int to = g.edge[j].to;
  56. if (gp[to] != gp[i] && !hst.count(make_pair(gp[i],gp[to]))) {
  57. hst.insert(make_pair(gp[i], gp[to]));
  58. g1.push(gp[i], gp[to]);
  59. }
  60. }
  61. }
  62. work();
  63. return 0;
  64. }

bzoj2330: [SCOI2011]糖果

缩点建图,取等号的边长为0,不取的边长为1。不能满足当且仅当存在长度大于1的环。
然后缩完点就变成dag上最长链了。dp做就可以。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 100005, MAXM = 200005;
  4. struct graph {
  5. struct node {
  6. int to, next, dis;
  7. } edge[MAXM];
  8. int head[MAXN], top;
  9. graph(){top = 0; memset(head, 0, sizeof head);}
  10. void push(int i, int j, int d)
  11. { //printf("%d--%d-->%d\n", i, d, j);
  12. ++top, edge[top] = (node) {j, head[i], d}, head[i] = top; }
  13. }g, g1;
  14. int n, m;
  15. int dfn[MAXN], low[MAXN], prelen[MAXN], stk[MAXN], vis[MAXN], top = 0, dft = 0;
  16. int gp[MAXN], gsiz[MAXN], gtop = 0, len_end[MAXN];
  17. int fail = 0;
  18. void tarjan(int nd)
  19. {
  20. dfn[nd] = low[nd] = ++dft, stk[++top] = nd, vis[nd] = 1;
  21. for (int i = g.head[nd]; i; i = g.edge[i].next) {
  22. int to = g.edge[i].to, d = g.edge[i].dis;
  23. if (!dfn[to]) prelen[to] = d, tarjan(to), low[nd] = min(low[nd], low[to]);
  24. else if (vis[to]) low[nd] = min(low[nd], dfn[to]), len_end[MAXN] += d;
  25. }
  26. if (dfn[nd] == low[nd]) {
  27. ++gtop; int now, len = len_end[MAXN];
  28. // printf("--- Group %d ---\n", gtop);
  29. do {
  30. now = stk[top--], vis[now] = 0;
  31. // printf("%d ", now);
  32. gp[now] = gtop, gsiz[gtop]++, len += prelen[now]*(now!=nd);
  33. } while (now != nd); //puts("");
  34. if (len != 0) {fail = 1; return;}
  35. }
  36. }
  37. int dp[MAXN];
  38. int dfs(int nd)
  39. {
  40. if (dp[nd] != -1) return dp[nd];
  41. dp[nd] = 0;
  42. for (int i = g1.head[nd]; i; i = g1.edge[i].next) {
  43. int to = g1.edge[i].to, d = g1.edge[i].dis;
  44. dp[nd] = max(dp[nd], dfs(to)+d);
  45. }
  46. return dp[nd] == 0 ? dp[nd] = 1 : dp[nd];
  47. }
  48. void work()
  49. {
  50. for (int i = 1; i <= n; i++)
  51. for (int j = g.head[i]; j; j = g.edge[j].next) {
  52. int to = g.edge[j].to, d = g.edge[j].dis;
  53. if (gp[i] != gp[to])
  54. g1.push(gp[to], gp[i], d); // 反图
  55. }
  56. memset(dp, -1, sizeof dp);
  57. long long ans = 0;
  58. for (int i = 1; i <= gtop; i++)
  59. ans += dfs(i)*(long long)gsiz[i];
  60. printf("%lld\n", ans);
  61. }
  62. int main()
  63. {
  64. scanf("%d%d", &n, &m);
  65. for (int i = 1; i <= m; i++) {
  66. int x, u, v; scanf("%d%d%d", &x, &u, &v);
  67. switch (x) {
  68. case 1: g.push(u, v, 0), g.push(v, u, 0); break;
  69. case 2: g.push(u, v, 1); break;
  70. case 3: g.push(v, u, 0); break;
  71. case 4: g.push(v, u, 1); break;
  72. case 5: g.push(u, v, 0); break;
  73. }
  74. }
  75. for (int i = 1; i <= n; i++)
  76. if (!dfn[i])
  77. tarjan(i);
  78. if (fail == 1) puts("-1");
  79. else work();
  80. return 0;
  81. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注