[关闭]
@2368860385 2020-11-07T03:05:15.000000Z 字数 9985 阅读 182

day4

正睿提高

2018.9.15


放假回家,然后没做。。。
回来看了题之后,发现一个都不会做。。。

T1

每个点只能买或者卖。
第一档:3^n枚举每个点是买/卖/不操作

第二档:状压前面没有买的点。

第三档:dp,f[i][j]表示到第i个位置,买了j个的获益。然后判断i是买/卖/不操作。个人理解:第二维在转移时好像并没有用,因为这j个也不知道是哪些,获得的收益也不知道从哪里买的。但是去掉这一维发现不能转移了(转移时不会买,因为一旦买就成了负数了),转移时i只有三种情况买/卖/不操作,为了可以转移,必须要满足三种情况可以转移(从哪转移,贡献怎么算),发现转移会影响当前买的个数,于是加上这个限制条件就可以转移了。

第四档:直接判断第i个位置是否可以卖出。然后从前面所有没有买的找最小的,从所有卖出的位置替换(表示在i这个位置卖),如果都不能获得收益,则只能在i这里买了。注意:为了让操作次数更少,于是先考虑在i卖出,可能代替某些位置。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<queue>
  7. using namespace std;
  8. typedef long long LL;
  9. inline int read() {
  10. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  11. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  12. }
  13. const int N = 50010;
  14. int a[N];
  15. int n;
  16. priority_queue< int, vector<int>, greater<int> > q1, q2;
  17. void init() {
  18. n = read();
  19. for (int i=1; i<=n; ++i) a[i] = read();
  20. while (!q1.empty()) q1.pop();
  21. while (!q2.empty()) q2.pop();
  22. }
  23. void solve() {
  24. LL Ans = 0;
  25. for (int i=1; i<=n; ++i) {
  26. int opt = 0, mx = 0;
  27. if (!q2.empty()) { // 卖出的,优先考虑,减少购买次数
  28. if (mx < a[i] - q2.top())
  29. mx = a[i] - q2.top(), opt = 1;
  30. }
  31. if (!q1.empty()) { // 未买入的
  32. if (mx < a[i] - q1.top())
  33. mx = a[i] - q1.top(), opt = 2;
  34. }
  35. Ans += mx;
  36. if (opt == 0) q1.push(a[i]);
  37. else if (opt == 1) q1.push(q2.top()), q2.pop(), q2.push(a[i]);
  38. else q1.pop(), q2.push(a[i]);
  39. }
  40. cout << Ans << " " << (int)(q2.size()) * 2 << "\n";
  41. }
  42. int main() {
  43. int T = read();
  44. while (T--) {
  45. init();
  46. solve();
  47. }
  48. return 0;
  49. }

T2

二分一个直径d,判断能否穿过。
判断是否穿过有点难,正难则反,判断是否不能穿过。
假设L上方有一个点为S,x轴下方有一个点为T,如果两点之间的距离小于d,那么连一条边,如果S与T联通了,那么说明直径为d的球无法穿过。复杂度

也就是求最小瓶颈生成树。

瓶颈生成树 :无向图G的一颗瓶颈生成树是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。
无向图的最小生成树一定是瓶颈生成树。瓶颈生成树不一定是最小生成树。

  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. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. const double eps = 1e-8;
  16. const int N = 505;
  17. int fa[N];
  18. int n, S, T;
  19. double x[N], y[N], L;
  20. int find(int x) {
  21. return x == fa[x] ? x : fa[x] = find(fa[x]);
  22. }
  23. void Merge(int x,int y) {
  24. x = find(x), y = find(y);
  25. fa[x] = y;
  26. }
  27. double Calc(int i,int j) {
  28. return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
  29. }
  30. bool check(double d) {
  31. for (int i=1; i<=n+2; ++i) fa[i] = i;
  32. for (int i=1; i<=n; ++i) {
  33. if (d > y[i]) Merge(T, i); // 居然写成了x[i]
  34. if (d > L - y[i]) Merge(S, i);
  35. for (int j=i+1; j<=n; ++j)
  36. if (d > Calc(i, j)) Merge(i, j);
  37. }
  38. if (find(S) == find(T)) return false;
  39. return true;
  40. }
  41. int main() {
  42. scanf("%d%lf",&n,&L);
  43. S = n + 1, T = n + 2;
  44. for (int i=1; i<=n; ++i)
  45. scanf("%lf%lf",&x[i],&y[i]);
  46. double l = 0, r = L, ans = 0;
  47. while (r - l > eps) {
  48. double mid = (l + r) / 2.0;
  49. if (check(mid)) ans = mid, l = mid;
  50. else r = mid;
  51. }
  52. printf("%.3lf",ans);
  53. return 0;
  54. }

T3

题意:一个长度为N的01序列。
现在有Q次询问,每次询问一个区间[L,R],问在这个区间中最少需要删掉多少个数,使得这个区间剩下的每个非空前缀以及每个非空后缀中,0的个数都不小于1的个数。
(老是想成将1变成0。。。)

两种做法。
首先都是将字符串中的0变成1,1变成-1。不合法=>区间和为负
1、线段树求最小的“前缀+后缀”
结论:答案就是在查询区间内,选一个前缀,一个后缀(要求不重复),然后加起来,取最小值。
就是
证明?(测试一下小于1000的数据?)

于是就可以线段树维护了。
首先让每个节点的值直接是前缀和,和后缀和,最后答案=ans-pre[l-1]-suf[r+1]就行了。
每个节点维护三个值:vl,vr,val,表示在此区间的前缀最大值,后缀最大值,前缀和后缀都在此区间的答案的最大值(l,r都在此区间的,才更新)。
查询的时候是这样的:
_ | _ _ | _ _ _ _
l,r都在左边的线段树维护了,都在右边的也维护了。还有一个在左边一个在右边,这样没有更新到。由于每次更新一段区间一定是从左往右更新的,那么记录一个值表示当前区间的左边的所有区间内的前缀最大值(比如上图中到第三个区间的时候,更新出了前两个区间的前缀最大值,即123),然后可以直接和这个区间的后缀最大值合并了。

2、倍增

每次询问:从左忘右扫一遍,删除必须要删除的1,(为了是所有非空前缀满足条件),然后为了让所有非空后缀满足条件,然后在删掉1后的序列上,求一个最小后缀和,ans=删掉的1的个数+(-最小后缀和)

题解:
考虑用倍增加速删除的过程。
每个点记录右边第一个删除的位置,显然是一个树结构。
然后删除这个点后,剩下很多段,然后合并这些段求出最小后缀值。

011100111
0走到第3个位置,剩下的段是01,然后在走到第4个位置,没有剩下的,然后再走到最后一个1的位置,剩下的是0011,合起来就是010011,求出最小后缀和。
两段合并的过程是可以倍增加速的。就是一次走2^j步。最后一个段不一定走完,特判。

线段树维护前缀最小 + 后缀最小

  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. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. const int N = 200005;
  20. const int INF = 1e9;
  21. char s[N];
  22. int a[N], val[N << 2], vl[N << 2], vr[N << 2], pre[N], suf[N];
  23. int Ans, VL;
  24. void solve1(int n, int Q) {
  25. while (Q--) {
  26. int l = read(), r = read(), ans = INF, minL = INF;
  27. for (int sum=0,i=l-1; i<=r; ++i) {
  28. minL = min(minL, pre[i] - pre[l - 1]);
  29. ans = min(ans, minL + (suf[i + 1] - suf[r + 1]));
  30. }
  31. printf("%d\n",-ans);
  32. }
  33. }
  34. #define Root 0, n + 1, 1
  35. #define lson l, mid, rt << 1
  36. #define rson mid + 1, r, rt << 1 | 1
  37. void pushup(int rt) {
  38. vl[rt] = min(vl[rt << 1], vl[rt << 1 | 1]);
  39. vr[rt] = min(vr[rt << 1], vr[rt << 1 | 1]);
  40. val[rt] = min(vl[rt << 1] + vr[rt << 1 | 1], min(val[rt << 1], val[rt << 1 | 1]));
  41. }
  42. void build(int l,int r,int rt) {
  43. if (l == r) {
  44. vl[rt] = pre[l], vr[rt] = suf[l], val[rt] = INF;
  45. return ;
  46. }
  47. int mid = (l + r) >> 1;
  48. build(lson);build(rson);
  49. pushup(rt);
  50. }
  51. void query(int l,int r,int rt,int L,int R) {
  52. if (L <= l && r <= R) {
  53. if (VL == INF) Ans = val[rt], VL = vl[rt];
  54. else {
  55. Ans = min(Ans, val[rt]);
  56. Ans = min(Ans, VL + vr[rt]);
  57. VL = min(VL, vl[rt]);
  58. }
  59. return ;
  60. }
  61. int mid = (l + r) >> 1;
  62. if (L <= mid) query(lson, L, R);
  63. if (R > mid) query(rson, L, R);
  64. }
  65. int main() {
  66. int n = read(), Q = read();
  67. scanf("%s",s + 1);
  68. for (int i=1; i<=n; ++i)
  69. a[i] = s[i] == '0' ? 1 : - 1;
  70. for (int i=1; i<=n; ++i) pre[i] = pre[i - 1] + a[i];
  71. for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i];
  72. if (n <= 1000) { solve1(n, Q); return 0; }
  73. build(Root);
  74. while (Q --) {
  75. int l = read(), r = read();
  76. l --, r ++;
  77. VL = Ans = INF;
  78. query(Root, l, r);
  79. Ans = Ans - pre[l] - suf[r];
  80. printf("%d\n",-Ans);
  81. }
  82. return 0;
  83. }

倍增。

  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. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. const int N = 200005;
  20. const int INF = 1e9;
  21. char s[N];
  22. int a[N], pre[N], suf[N], sk[N], st[N][20];
  23. struct Node{
  24. int pos, sum, val, cnt; // 分别表示位置,和,最小后缀和,要删掉个数。
  25. }f[N][20];
  26. Node operator + (Node A, Node B) {
  27. Node C;
  28. C.pos = B.pos;
  29. C.sum = A.sum + B.sum;
  30. C.val = min(B.val, B.sum + A.val);
  31. C.cnt = A.cnt + B.cnt;
  32. return C;
  33. }
  34. int getsum(int l,int r) {
  35. if (l > r) return 0;
  36. return suf[l] - suf[r + 1];
  37. }
  38. int getmin(int l,int r) {
  39. if (l > r) return INF;
  40. int t = log2(r - l + 1);
  41. return min(st[l][t], st[r - (1<<t) + 1][t]);
  42. }
  43. void work() {
  44. int l = read(), r = read();
  45. if (l == r) {
  46. if (s[l] == '1') puts("1");
  47. else puts("0");
  48. return ;
  49. }
  50. Node ans;
  51. ans.pos = l; ans.val = INF; ans.sum = 0; ans.cnt = 0;
  52. for (int j=19; j>=0; --j)
  53. if (f[ans.pos][j].pos <= r)
  54. ans = ans + f[ans.pos][j];
  55. if (ans.pos < r) {
  56. int sum = getsum(ans.pos + 1, r);
  57. int mn = getmin(ans.pos + 1, r) - suf[r + 1];
  58. ans.val = min(0, min(ans.val + sum, mn));
  59. }
  60. if (a[l] == -1) ans.cnt ++;
  61. if (ans.cnt == (r-l+1)) ans.val = 0;
  62. printf("%d\n", ans.cnt + (-ans.val));
  63. }
  64. int main() {
  65. int n = read(), Q = read();
  66. scanf("%s",s + 1);
  67. for (int i=1; i<=n; ++i)
  68. a[i] = s[i] == '0' ? 1 : - 1;
  69. memset(st, 0x3f, sizeof(st));
  70. for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i], st[i][0] = suf[i];
  71. for (int j=1; j<=19; ++j)
  72. for (int i=1; (i+(1<<(j-1)))<=n; ++i) {
  73. st[i][j] = min(st[i][j - 1], st[i + (1<<(j-1))][j - 1]);
  74. }
  75. int top = 1;sk[top] = n + 1;
  76. for (int p,i=n; i>=1; --i) {
  77. if (s[i] == '0') {
  78. if (top > 1) top --;
  79. }
  80. p = sk[top];
  81. f[i][0].pos = p, f[i][0].sum = getsum(i + 1, p - 1), f[i][0].val = getmin(i + 1, p - 1) - suf[p];
  82. f[i][0].cnt = 1;
  83. if (s[i] == '1') sk[++top] = i;
  84. }
  85. for (int j=0; j<=19; ++j) f[n+1][j].pos = n + 1;
  86. for (int j=1; j<=19; ++j)
  87. for (int i=1; i<=n; ++i)
  88. f[i][j] = f[i][j - 1] + f[f[i][j-1].pos][j - 1];
  89. while (Q--) work();
  90. return 0;
  91. }

调试代码

  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. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. const int N = 200005;
  20. const int INF = 1e9;
  21. char s[N];
  22. int a[N], pre[N], suf[N], sk[N], st[N][20];
  23. struct Node{
  24. int pos, sum, val, cnt;
  25. }f[N][20];
  26. Node operator + (Node A, Node B) {
  27. Node C;
  28. C.pos = B.pos;
  29. C.sum = A.sum + B.sum;
  30. C.val = min(B.val, B.sum + A.val);
  31. C.cnt = A.cnt + B.cnt;
  32. return C;
  33. }
  34. void solve1(int n, int Q) {
  35. while (Q--) {
  36. int l = read(), r = read(), ans = 0;
  37. for (int sum=0,i=l; i<=r; ++i) {
  38. sum += a[i];
  39. if (sum < 0) sum = 0, ans ++, a[i] = 0;
  40. }
  41. for (int sum=0,i=r; i>=l; --i) {
  42. sum += a[i];
  43. if (sum < 0) sum = 0, ans ++, a[i] = 0;
  44. }
  45. for (int i=l; i<=r; ++i)
  46. if (!a[i]) a[i] = -1;
  47. printf("%d\n",ans);
  48. }
  49. }
  50. int getsum(int l,int r) {
  51. if (l > r) return 0;
  52. return suf[l] - suf[r + 1];
  53. }
  54. int getmin(int l,int r) {
  55. if (l > r) return INF;
  56. int t = log2(r - l + 1);
  57. return min(st[l][t], st[r - (1<<t) + 1][t]);
  58. }
  59. void work() {
  60. int l = read(), r = read();
  61. if (l == r) {
  62. if (s[l] == '1') puts("1");
  63. else puts("0");
  64. return ;
  65. }
  66. Node ans;
  67. ans.pos = l; ans.val = INF; ans.sum = 0; ans.cnt = 0;
  68. for (int j=19; j>=0; --j) {
  69. // cout << f[ans.pos][j].pos << "\n";
  70. if (f[ans.pos][j].pos <= r) {
  71. ans = ans + f[ans.pos][j];
  72. // cout << ans.pos << " "<< ans.sum << " " << ans.cnt << " " << ans.val << "\n";
  73. }
  74. }
  75. // cout << ans.sum << " " << ans.cnt << " " << ans.val << "\n";
  76. if (ans.pos < r) {
  77. int sum = getsum(ans.pos + 1, r);
  78. int mn = getmin(ans.pos + 1, r) - suf[r + 1];
  79. ans.val = min(0, min(ans.val + sum, mn));
  80. }
  81. if (a[l] == -1) ans.cnt ++;
  82. if (ans.cnt == (r-l+1)) ans.val = 0;
  83. printf("%d\n", ans.cnt + (-ans.val));
  84. }
  85. //#define bd
  86. int main() {
  87. #ifdef bd
  88. fi("2.txt");
  89. #endif
  90. int n = read(), Q = read();
  91. scanf("%s",s + 1);
  92. for (int i=1; i<=n; ++i)
  93. a[i] = s[i] == '0' ? 1 : - 1;
  94. // if (n <= 1000) { solve1(n, Q); return 0; }
  95. memset(st, 0x3f, sizeof(st));
  96. for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i], st[i][0] = suf[i];
  97. for (int j=1; j<=19; ++j)
  98. for (int i=1; (i+(1<<(j-1)))<=n; ++i) {
  99. st[i][j] = min(st[i][j - 1], st[i + (1<<(j-1))][j - 1]);
  100. }
  101. int top = 1;sk[top] = n + 1;
  102. for (int p,i=n; i>=1; --i) {
  103. if (s[i] == '0') {
  104. if (top > 1) top --;
  105. }
  106. p = sk[top];
  107. f[i][0].pos = p, f[i][0].sum = getsum(i + 1, p - 1), f[i][0].val = getmin(i + 1, p - 1) - suf[p];
  108. f[i][0].cnt = 1;
  109. // cout << p << " " << f[i][0].sum << " " << f[i][0].val << " \n";
  110. if (s[i] == '1') sk[++top] = i;
  111. }
  112. for (int j=0; j<=19; ++j) f[n+1][j].pos = n + 1;
  113. for (int j=1; j<=19; ++j)
  114. for (int i=1; i<=n; ++i) {
  115. f[i][j] = f[i][j - 1] + f[f[i][j-1].pos][j - 1];
  116. // cout << i << " " << f[i][j].pos << "\n";
  117. // cout << f[i][j].val << " " << f[i][j].sum << "\n";
  118. }
  119. while (Q--) work();
  120. return 0;
  121. }
  122. /*
  123. 11 1
  124. 01110100111
  125. */

对拍数据生成器

  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. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. #include<ctime>
  14. using namespace std;
  15. typedef long long LL;
  16. inline int read() {
  17. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  18. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  19. }
  20. int a[200005];
  21. int main() {
  22. srand(time(0));
  23. int n = 997, q = 1000;
  24. cout << n << " " << q << "\n";
  25. int t = rand() % n + 1;
  26. for (int i=1; i<=t; ++i) a[i] = 1;
  27. random_shuffle(a + 1, a + n + 1);
  28. for (int i=1; i<=n; ++i) cout << a[i];
  29. puts("");
  30. for (int i=1; i<=q; ++i) {
  31. int l = rand() % n + 1;
  32. int r = rand() % (n - l + 1) + l;
  33. cout << l << " " << r << "\n";
  34. }
  35. return 0;
  36. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注