[关闭]
@2368860385 2018-09-01T03:11:50.000000Z 字数 4161 阅读 175

正睿提高十连测day1——2018.8.25

比赛总结


1

找规律。最优一定是围成六边形。

  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. const int N = 1000100;
  14. LL f[N], g[N];
  15. int cnt;
  16. void init() {
  17. f[1] = 1;f[2] = 6;
  18. g[1] = 1;g[2] = 7;
  19. for (cnt=3; cnt<=1000000; ++cnt) {
  20. f[cnt] = f[cnt - 1] + 6;
  21. g[cnt] = g[cnt - 1] + f[cnt];
  22. }
  23. }
  24. int main() {
  25. LL n; cin >> n;
  26. init();
  27. if (n == 1) {
  28. cout << 6; return 0;
  29. }
  30. LL t, t2, tmp;
  31. for (int i=1; i<=cnt; ++i) {
  32. if (g[i] == n) {
  33. cout << f[i + 1]; return 0;
  34. }
  35. else if (g[i] > n) {
  36. t = i - 1;
  37. break;
  38. }
  39. }
  40. LL r = n - g[t], m = t, ans = 0;
  41. if (r <= (m - 1)) {
  42. ans += (f[t + 1] - r) + r + 1;
  43. }
  44. else {
  45. // tmp = r - (m - 1);
  46. t2 = r / m;
  47. ans += (f[t + 1] - r) + r + t2 + 1;
  48. }
  49. cout << ans;
  50. return 0;
  51. }

T2

按钮i连向一个物品j,那么增加一条(j->i)的边,说明按完j按钮后才能按i(先按i的话,j就不能按了),(自己连向自己的可以跳过,直接统计答案,防止自环)。
如果物品j有多个连向的按钮,按最大的。
然后每个按钮指向一个物品,每个物品最多一个按钮。所以是一条链或者一个环。链上的情况是都能取到的(全部按完)。
而环上的一个物品是不能取到的,他可以由连向它的第二大的取。每个物品在记录连向它的第二大的边。于是在这些第二大的边中找最大的(可能没有)。

这个地方考试时想错了,每条边只连向最大的,所以形成了基环树。(开始没想到,最后想到后,没时间改了,T1做的太长了。)

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<cctype>
  6. using namespace std;
  7. typedef long long LL;
  8. inline int read() {
  9. int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch=getchar()) if (ch=='-') f = -1;
  10. for (; isdigit(ch); ch=getchar()) x = x * 10 + ch - '0'; return x * f;
  11. }
  12. const int N = 200100;
  13. int f[N], vis[N];
  14. LL c[N], d[N], a[N], fir[N], sec[N];
  15. int Now_Color;
  16. LL Answer, Min;
  17. void dfs(int u) {
  18. if (vis[u] == Now_Color) { Answer -= Min; return ;}
  19. if (vis[u]) return ;
  20. vis[u] = Now_Color;
  21. if (fir[u]) {
  22. Answer += 1ll * c[fir[u]] * a[u];
  23. Min = min(Min, c[fir[u]] - c[sec[u]]);
  24. if (fir[u] != u) dfs(fir[u]);
  25. }
  26. }
  27. int main() {
  28. int n = read();
  29. for (int i=1; i<=n; ++i)
  30. f[i] = read(), c[i] = read(), d[i] = read(), a[i] = read();
  31. for (int i=1; i<=n; ++i) {
  32. c[i] = d[f[i]] - c[i];
  33. if (c[i] > c[fir[f[i]]]) sec[f[i]] = fir[f[i]], fir[f[i]] = i;
  34. else if (c[i] > c[sec[f[i]]]) sec[f[i]] = i;
  35. }
  36. for (int i=1; i<=n; ++i)
  37. if (!vis[i]) ++Now_Color, Min = 1e9, dfs(i);
  38. cout << Answer;
  39. return 0;
  40. }

T3

n^2枚举一个字符串,(优化:预处理n的因数),然后判断(优化:根据字符的个数)。

判断:
转化,插入一个字符串->消光字符串。
设枚举的原始串为p,长度为len
发现原始串p被分为几段后,中间的每一段都是可以独立消光的一段。
原始串为,a....b....cd,原始串被划为3段,但中间的都是可以消光。
f[i][j]表示i~j是否合法。
如果j-i+1为len:则表示能否消光。
否则表示多余的部分是否是p的前缀(也就是去掉所有可以消掉的,剩下的合起来是p的前缀a.....b....c,去掉所有消掉的,还剩abc是abcd的前缀,所以合法)

然后可以加一个字符一个字符的加,然后判断新的形成的区间是否合法(就是f[i][j]从f[i][j-1]转移,f[i][j-1]合法了,加入字符j,判断f[i][j]),如果加上这个字符,和剩下的还是可以组成p的后缀,那么就是合法的。
当然还有一种情况就是就是最后加入一个字符后,使得字符串形成了一个以j结尾的,长度为len*k的可以消掉的字符串,那么前面的就可以和这个字符串合起来了。就是f[i][j-len*k](合法)和f[j-len*k+1][j]合起来,f[i][j]就是合法的,当前的前缀就是f[i][j-len*k]的剩余的字符。
样例abaabcdbcd,最后加入d后,后面8个形成了可消,和前面ab合起来就行了。
详见代码

  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 = 205;
  18. const int mod = 12582917;
  19. const int Base = 131;
  20. bool vis[mod + 10];
  21. int f[N][N], cnt1[N], cnt2[N], num[N];
  22. char p[N], s[N], ans[N];
  23. int n;
  24. vector<int> q;
  25. int getHash(int len) {
  26. int ans = 0;
  27. for (int i=1; i<=len; ++i)
  28. ans = 1ll * ans * Base % mod + (p[i] - 'a' + 1) % mod;
  29. return ans;
  30. }
  31. void Update(int len) {
  32. if (ans[1] == '#')
  33. for (int i=1; i<=len; ++i) ans[i] = p[i];
  34. else {
  35. for (int i=1; i<=len; ++i) if (p[i] > ans[i]) return ;
  36. for (int i=1; i<=len; ++i) ans[i] = p[i];
  37. }
  38. }
  39. bool Solve(int len) {
  40. for (int i=1; i<=n; ++i) f[i][i] = s[i]==p[1];
  41. for (int L=2; L<=n; ++L) {
  42. for (int i=1,j; (j=i+L-1)<=n; ++i) {
  43. f[i][j] = 0;
  44. if (f[i][j - 1] && s[j]==p[(L-1)%len+1]) f[i][j] = 1;
  45. else if(s[j]==p[len]) for (int k=1; i+k*len<=j; ++k) {
  46. if (f[i][j-k*len] && f[j-k*len+1][j]) {f[i][j] = 1; break; }
  47. }
  48. }
  49. }
  50. return f[1][n];
  51. }
  52. bool getsub(int l,int len) {
  53. for (int i='a'; i<='z'; ++i) cnt2[i] = 0;
  54. for (int i=1; i<=len; ++i) p[i] = s[l + i - 1], cnt2[p[i]] ++;
  55. for (int i='a'+1; i<='z'; ++i) if (cnt2[i] && cnt1[i] % cnt2[i]) return false;
  56. int H = getHash(len);
  57. if (vis[H]) return false;
  58. vis[H] = true; q.push_back(H);
  59. return true;
  60. }
  61. int main() {
  62. int Case = read();
  63. while (Case --) {
  64. scanf("%s", s + 1);
  65. n = strlen(s + 1);
  66. ans[1] = '#';
  67. for (int i=1; i<=n; ++i) ++cnt1[s[i]];
  68. int tot = 0;
  69. for (int i=1,lim=sqrt(n); i<=lim; ++i)
  70. if (n % i == 0) {
  71. num[++tot] = i;
  72. if (n / i != i) num[++tot] = n / i;
  73. }
  74. sort(num + 1, num + tot + 1);
  75. for (int t=1,len=num[t]; t<=tot; len=num[++t]) {
  76. for (int l=1; (l+len-1)<=n; ++l) {
  77. if (!getsub(l, len)) continue;
  78. if (Solve(len)) Update(len);
  79. }
  80. if (ans[1] != '#') {
  81. for (int i=1; i<=len; ++i) putchar(ans[i]); puts(""); break;
  82. }
  83. }
  84. for (int i='a'; i<='z'; ++i) cnt1[i] = 0;
  85. for (int i=0,sz=q.size(); i<sz; ++i) vis[q[i]] = false;
  86. q.clear();
  87. }
  88. return 0;
  89. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注