[关闭]
@2368860385 2020-11-07T03:05:08.000000Z 字数 3268 阅读 183

day3

正睿提高

2018.9.8


预计:100+100+70
实际:100+100+70

有点zz,题3上来就想到了n^3区间dp,然后却没想到枚举左右端点。
然后最后回一区的路上想起了T3的正解。

T1

字典序最小,所以维护两个指针,先放小的。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  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 = 200010;
  18. int a[N], b[N], c[N];
  19. int main() {
  20. int n = read(), m = read();
  21. for (int i=1; i<=m; ++i) {
  22. a[i] = read(); c[a[i]] = 1;
  23. }
  24. int p = 0;
  25. for (int i=1; i<=n; ++i) {
  26. if (!c[i]) b[++p] = i;
  27. }
  28. // puts("");
  29. int p1 = 1, p2 = 1;
  30. while (p1 <= m && p2 <= p) {
  31. if (a[p1] < b[p2]) {
  32. printf("%d\n",a[p1]);
  33. p1 ++;
  34. }
  35. else {
  36. printf("%d\n",b[p2]);
  37. p2 ++;
  38. }
  39. }
  40. for (int i=p1; i<=m; ++i) printf("%d\n",a[i]);
  41. for (int i=p2; i<=p; ++i) printf("%d\n",b[i]);
  42. return 0;
  43. }

T2

一个数轴,很多线段。很多给定的点。对于线段有多少种选择方案,使选的线段在同一个点上。

如果只有一个点,那么就是线段的所有的组合方式。两个点并且没有个一个线段覆盖两个点话,也可以直接2^k求。现在就是求一些线段覆盖了很多个点的时候,不重复的计算。

对于一个点上的很多线段,有x条是第一次出现,y条是以前出现了的(假设y条在上一个点计算了在上一个点的贡献,只考虑这个点的贡献)。那么x条就是随便选。y条那么考虑它和x一起组合的方案,就是

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  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 = 200100;
  18. const LL mod = 998244353;
  19. struct Edge{
  20. int l,r;
  21. bool operator < (const Edge &A) const {
  22. return r < A.r;
  23. }
  24. }A[N];
  25. int B[N];
  26. int s[N << 2], C[N << 2];
  27. bool pd[N << 2];
  28. vector<int> R[N << 2];
  29. LL mi[N];
  30. int main() {
  31. int n = read(), m = read(), cnt = 0;
  32. mi[0] = 1;
  33. for (int i=1; i<=n; ++i) mi[i] = 1ll * mi[i - 1] * 2 % mod;
  34. for (int i=1; i<=n; ++i) {
  35. A[i].l = read(), A[i].r = read();
  36. s[++cnt] = A[i].l; s[++cnt] = A[i].r;
  37. }
  38. for (int i=1; i<=m; ++i) {
  39. B[i] = read(); s[++cnt] = B[i];
  40. }
  41. sort(s + 1, s + cnt + 1);
  42. int lim = cnt; cnt = 1;
  43. for (int i=2; i<=lim; ++i) if (s[i] != s[cnt]) s[++cnt] = s[i];
  44. for (int i=1; i<=n; ++i) {
  45. A[i].l = lower_bound(s + 1, s + cnt + 1, A[i].l) - s;
  46. A[i].r = lower_bound(s + 1, s + cnt + 1, A[i].r) - s;
  47. C[A[i].l] ++; C[A[i].r + 1] --;
  48. R[A[i].l].push_back(A[i].r);
  49. }
  50. for (int i=1; i<=m; ++i)
  51. B[i] = lower_bound(s + 1, s + cnt + 1, B[i]) - s;
  52. sort(B + 1, B + m + 1);
  53. int pos = 1, now = 0;
  54. LL Ans = 0;
  55. for (int i=1; i<=m; ++i) {
  56. int add = 0;
  57. while (pos <= B[i]) {
  58. now += C[pos];
  59. for (int sz=R[pos].size(),j=0; j<sz; ++j)
  60. if (R[pos][j] >= B[i]) add++;
  61. pos ++;
  62. }
  63. Ans = (Ans + 1ll * (mi[add] - 1)) % mod;
  64. Ans = (Ans + 1ll * (mi[add] - 1) * (mi[now - add] - 1) % mod) % mod;
  65. }
  66. cout << Ans % mod;
  67. return 0;
  68. }
  69. /*
  70. 7 2
  71. 1 2
  72. 1 2
  73. 2 4
  74. 2 4
  75. 2 4
  76. 4 5
  77. 4 5
  78. 2 4
  79. */

T3

求多少合法的括号序列,括号有26种。

就是求多少个区间中的匹配是合法的。首先n^3dp。然后n^2的更简单,直接枚举左右端点,用一个栈判断。

考虑O(n)的做法。假设一个匹配时这样的,a......a....a那么第一个a只要找到它往右第一个合法的匹配(就是第二个a),然后直接加上第二个a的合法匹配就行了。那么如何找第一个合法匹配呢。
ab....ba
从后往前,假设后面的已经找到了最近的合法匹配,并保存了下来,然后可以直接判断它的最近的合法匹配的下一位是否与a相同(上面就是b找到第二个b,然后a判断第二个b的下一位)。
ab....bc..ca
如果一样,那么在找下一位的最近的合法匹配(就是c找到下一个c)
其中b....b和c..c都是合法的两段,它们不影响。

  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;
  10. char ch=getchar();
  11. for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  12. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
  13. return x*f;
  14. }
  15. const int N = 1000100;
  16. char s[N];
  17. int pos[N];
  18. LL cnt[N];
  19. void solve(int n) {
  20. LL Ans = 0;
  21. pos[n] = n + 1;
  22. for (int i=n; i>=1; --i) {
  23. int j = i + 1;
  24. while (j <= n && s[i] != s[j]) j = pos[j] + 1;
  25. pos[i] = j > n ? n + 1 : j;
  26. if (j <= n) cnt[i] += cnt[j + 1] + 1;
  27. Ans += cnt[i];
  28. }
  29. cout << Ans;
  30. }
  31. int main() {
  32. scanf("%s",s+1);
  33. int n = strlen(s + 1);
  34. solve(n);
  35. return 0;
  36. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注