[关闭]
@2368860385 2020-11-07T03:02:02.000000Z 字数 5113 阅读 171

后:day7

衢州

备注:下午下课后开始拉肚子,晚上去医院,没整理


预计:60+?+40
实际:60+10+40
第二题写了搜索,考完发现最后一个点是可以重复走的,代码中是不是重复走的,考试时过了小样例之后也没管。

T1花园

考试写了倍增,60分,处理只有一个询问的,单次处理nlogn,调了很久,其实如果把一个点拆成两个点做倍增的话,会简单些。
最后在倍增上bfs好像可以处理,最后没调出。

正解:
不啰唆了,代码注释。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<cctype>
  6. #include<cmath>
  7. #include<set>
  8. #include<map>
  9. #include<queue>
  10. #include<vector>
  11. using namespace std;
  12. typedef long long LL;
  13. inline int read() {
  14. int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch=getchar()) if (ch=='-') f = -1;
  15. for (; isdigit(ch); ch=getchar()) x = x * 10 + ch - '0'; return x * f;
  16. }
  17. const int N = 150010;
  18. struct Edge{
  19. int to, nxt, ty;
  20. Edge() {}
  21. Edge(int a,int c,int d) {to = a,ty = c,nxt = d;}
  22. }e[N << 1];
  23. int head[N], Enum;
  24. int sum[2][N << 2], Circle[2], cnt[2][N << 2], fir[N], dgr[N];
  25. int P;
  26. void add_edge(int u,int v,int w) {
  27. ++Enum; e[Enum] = Edge(v, w, head[u]); head[u] = Enum;
  28. }
  29. // w->u->v。原图:v->u->w
  30. //ty 0 w->u为最长边,下一条不必是最长的。原图:u->w走了最长的了,所以说v->u一定不能走u的最长边。
  31. //typ 1为从次长边过来 下条只能走最长边。原图:u->w走了次长的,所以说v->u一定走u的最长边。
  32. void dfs(int u,int dis,int ty,int dir) {
  33. if (!ty) cnt[dir][dis] ++; // 现在是走的反向,的就是从P往会走,所以如果上一次是走的最长边到达的u,就是u走的最长边到达上一个点
  34. for (int i=head[u]; i; i=e[i].nxt) {
  35. int v = e[i].to;
  36. if (!ty && v == fir[u] && dgr[u] > 1) continue;
  37. if (ty && v != fir[u]) continue;
  38. if (v == P && e[i].ty == dir) Circle[dir] = dis + 1; // 计算环的长度
  39. else dfs(v, dis + 1, e[i].ty, dir);
  40. }
  41. }
  42. int main() {
  43. int n = read(), m = read();P = read() + 1;
  44. //原图中,每个点只保留最大的边,和次大的边。真正建图时,建的反向边。
  45. for (int i=1; i<=m; ++i) {
  46. int u = read() + 1, v = read() + 1;
  47. if (!dgr[u]) add_edge(v, u, 0), dgr[u] ++, fir[u] = v; // 建反向边,fir[u]:原图中u走出的最大的点,反向图中到u的最大的点。
  48. else if (dgr[u] == 1) add_edge(v, u, 1), dgr[u] ++;
  49. if (!dgr[v]) add_edge(u, v, 0), dgr[v] ++, fir[v] = u;
  50. else if (dgr[v] == 1) add_edge(u, v, 1), dgr[v] ++;
  51. }
  52. dfs(P, 0, 0, 0); //反向搜索,从P出发,走非最长边,即原图中从非最长边到P,从最长边出
  53. if (dgr[P] > 1) dfs(P, 0, 1, 1); // 走最长边,原图中从最长边到P,从非最长边出
  54. for (int i=0,lim=(n<<2); i<lim; ++i) { // 所有能到P的点在2n步内一定可以到达P一次,所以2n之后,sum[0/1][i]=sum[0/1][i+Circle[0/1]] 预处理4n
  55. sum[0][i] = cnt[0][i]; // 第i个时刻,多少个点从非最长边到P
  56. if (Circle[0] && i - Circle[0] >= 0) sum[0][i] += sum[0][i - Circle[0]]; // 从非最长边到P后,存在环的情况,从P的最长边出
  57. sum[1][i] = cnt[1][i]; // 第i个时刻,多少个点从最长边到P
  58. if (Circle[1] && i - Circle[1] >= 0) sum[1][i] += sum[1][i - Circle[1]]; // 从最长边到P后,存在环的情况,从非最长边出
  59. }
  60. int Q = read();
  61. // 从2n往后,sum[0/1][i]=sum[0/1][i+Circle[0/1]],所以首先特判k是否小于2n,然后判断k>2n的情况试k回到2n~4n之间。
  62. // k一定是走了很多次圆圈,所以首先k%Circle,这是走圆圈之前走的步数,然后为了让k在[2n+1,4n],让圆圈的步数是2n内最大的。即2n/Circle*Circle
  63. while (Q --) {
  64. int k = read(), ans = 0, t;
  65. if (k <= n * 2) ans = sum[0][k] + sum[1][k];
  66. else {
  67. if (Circle[0]) {
  68. t = (2 * n) / Circle[0];
  69. ans += sum[0][k % Circle[0] + t * Circle[0]];
  70. }
  71. if (Circle[1]) {
  72. t = (2 * n) / Circle[1];
  73. ans += sum[1][k % Circle[1] + t * Circle[1]];
  74. }
  75. }
  76. printf("%d ",ans);
  77. }
  78. return 0;
  79. }

T2

写完T1的60,已经花费了不少时间了,看T2的时候,感觉不太会做,写了暴力没管,(考完试刚结束,发现了一句话“一个字符串可以重复经过,但只算一次”,发现写错了,本来以为30分,感觉变成了?)。
总结:

没有判断出难度,实际上比T1简单。以后多做题。应该仔细想想。

正解:tarjan缩环,然后DAG上最长路。
一个字符串看成一个点,经过操作后可以到的点,可以看成一条边,然后相当于在图上跑一个最长路。
求出所有的字符串,然后暴力建边。对于每个操作,枚举字符串,然后暴力建边。复杂度
发现第一个操作,交换相邻位置,实际上是没有用的,有用的只是ABCD的个数,一定个数的ABCD的一状态,通过交换相邻位置,可以得到ABCD个数不变的所有状态。(就是对于一个字符串s='AABBCDDD',如果这一状态可达,那么所有两个A两个B一个C三个D的字符串都可以由它交换相邻位置得到)。每个点只记录ABCD的个数。然后总点数是隔板法。每个点的权值就是ABCD这些个数的所有的排列。为
这个式子直接乘会爆,然后考虑这个式子怎么推。


  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<cctype>
  6. #include<cmath>
  7. #include<set>
  8. #include<map>
  9. #include<queue>
  10. #include<vector>
  11. using namespace std;
  12. typedef long long LL;
  13. inline int read() {
  14. int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch=getchar()) if (ch=='-') f = -1;
  15. for (; isdigit(ch); ch=getchar()) x = x * 10 + ch - '0'; return x * f;
  16. }
  17. const int N = 6000;
  18. vector<int> e[N], T[N];
  19. int dfn[N], low[N], sk[N], bel[N], dgr[N], q[N];
  20. bool vis[N];
  21. LL val[N], siz[N], dis[N], C[31][31];
  22. int id[31][31][31];
  23. int CntNode, Time_Index, tot, Top;
  24. void getnum(int *num) {
  25. char ch = getchar();
  26. while (ch != 'A' && ch!='B' && ch!='C' && ch!='D') ch = getchar();
  27. while (ch == 'A' || ch=='B' || ch=='C' || ch=='D') num[ch-'A'] ++, ch=getchar();
  28. }
  29. void init(int n) {
  30. for (int i=0; i<=n; ++i) {
  31. C[i][0] = 1;
  32. for (int j=1; j<=i; ++j)
  33. C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
  34. }
  35. for (int a=0; a<=n; ++a)
  36. for (int b=0; a+b<=n; ++b)
  37. for (int c=0; a+b+c<=n; ++c)
  38. id[a][b][c] = ++CntNode,
  39. val[CntNode] = C[a+b][b] * C[a+b+c][c] * C[n][n-a-b-c];
  40. }
  41. void tarjan(int u) {
  42. dfn[u] = low[u] = ++Time_Index;
  43. sk[++Top] = u;
  44. vis[u] = true;
  45. for (int sz=e[u].size(),i=0; i<sz; ++i) {
  46. int v = e[u][i];
  47. if (!dfn[v]) {
  48. tarjan(v);
  49. low[u] = min(low[u], low[v]);
  50. }
  51. else if (vis[v]) low[u] = min(low[u], dfn[v]);
  52. }
  53. if (dfn[u] == low[u]) {
  54. ++tot;
  55. do {
  56. siz[tot] += val[sk[Top]];
  57. vis[sk[Top]] = false;
  58. bel[sk[Top]] = tot;
  59. Top--;
  60. } while (sk[Top + 1] != u);
  61. }
  62. }
  63. LL topo() {
  64. int L = 1, R = 0;
  65. for (int i=1; i<=tot; ++i)
  66. if (!dgr[i]) q[++R] = i, dis[i] = siz[i];
  67. LL Ans = 0;
  68. while (L <= R) {
  69. int u =q[L ++];
  70. Ans = max(Ans, dis[u]);
  71. for (int sz=T[u].size(),i=0; i<sz; ++i) {
  72. int v = T[u][i];
  73. dis[v] = max(dis[v], dis[u] + siz[v]);
  74. if (!(--dgr[v])) q[++R] = v;
  75. }
  76. }
  77. return Ans;
  78. }
  79. int main() {
  80. freopen("1.txt","r",stdin);
  81. int n = read(), m = read();
  82. init(n);
  83. for (int i=1; i<=m; ++i) {
  84. int num1[4] = {0, 0, 0, 0}, num2[4] = {0, 0, 0, 0};
  85. getnum(num1); getnum(num2);
  86. if (num1[0] == num2[0] && num1[1] == num2[1] && num1[2] == num2[2]) continue;
  87. for (int a=num1[0]; a<=n; ++a) {
  88. for (int b=num1[1]; a+b<=n; ++b) {
  89. for (int c=num1[2]; a+b+c<=n; ++c) {
  90. if (n - a - b - c < num1[3]) break;
  91. e[id[a][b][c]].push_back(id[a-num1[0]+num2[0]][b-num1[1]+num2[1]][c-num1[2]+num2[2]]);
  92. }
  93. }
  94. }
  95. }
  96. for (int i=1; i<=CntNode; ++i)
  97. if (!dfn[i]) tarjan(i);
  98. for (int u=1; u<=CntNode; ++u) {
  99. for (int sz=e[u].size(),i=0; i<sz; ++i) {
  100. int v = e[u][i];
  101. if (bel[u] != bel[v]) T[bel[u]].push_back(bel[v]), dgr[bel[v]] ++;
  102. }
  103. }
  104. cout << topo();
  105. return 0;
  106. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注