飞道的博客

北京化工大学1/3寒假集训题解

324人阅读  评论(0)

1 A - Super Mario

这道题要求某区间内比h小的个数,其实这里可以类似于树状数组求逆序数那样。关键是如何转换成树状数组的模型,这才是本题的难点。

我们首先分析,如果知道h在该区间的哪个位置,那么剩下的就很好做了。我们还可以发现,如果找到了当前的比h小的所有点(大于的点我们先忽略掉),那么我们就可以用树状数组求它的[l,r]区间的和。这样就跟树状数组有了一点联系,但是还不够,因为我们发现,h的大小会影响我们所要找的区间。什么意思呢?就是我们先找到的是h1,再找h2,但h1>h2,就会出现这样的问题:h1所对应的区间找到了,但再找h2时,它对应的区间中可能会有比h2大的数,这样就不符合条件了。所以说这里有一个离线算法,就是先把所有的询问存下来,再按照h的大小进行从小到大排序,然后根据h的大小,从小到大地一个个插入所有区间内的数,这样就符合条件了。


  
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const int maxn = 1e5+ 5;
  7. struct Query
  8. {
  9. int l,r,h;
  10. int id;
  11. bool operator < (Query a)
  12. {
  13. return h < a.h;
  14. }
  15. }q[maxn];
  16. struct Node
  17. {
  18. int num;
  19. int id;
  20. bool operator < (Node a)
  21. {
  22. return num < a.num;
  23. }
  24. }a[maxn];
  25. int n,m;
  26. int tree[maxn<< 2],ans[maxn];
  27. int lowbit(int x)
  28. {
  29. return x & (-x);
  30. }
  31. void update(int x,int d)
  32. {
  33. for( int i = x; i <= n; i += lowbit(i))
  34. tree[i] += d;
  35. }
  36. int getsum(int x)
  37. {
  38. int sum = 0;
  39. for( int i = x; i > 0; i -= lowbit(i))
  40. sum += tree[i];
  41. return sum;
  42. }
  43. int main()
  44. {
  45. int t,cas = 1;
  46. cin >> t;
  47. while(t--)
  48. {
  49. cin >> n >> m;
  50. for( int i = 1; i <= n; i++)
  51. {
  52. cin >> a[i].num;
  53. a[i].id = i;
  54. }
  55. for( int i = 1; i <= m; i++)
  56. {
  57. cin >> q[i].l >> q[i].r >> q[i].h;
  58. q[i].l++, q[i].r++;
  59. q[i].id = i;
  60. }
  61. sort(a+ 1,a+ 1+n);
  62. sort(q+ 1,q+ 1+m);
  63. memset(tree, 0, sizeof(tree));
  64. int idx = 1;
  65. for( int i = 1; i <= m; i++)
  66. {
  67. while(q[i].h >= a[idx].num && idx <= n)
  68. {
  69. update(a[idx].id, 1);
  70. idx++;
  71. }
  72. ans[q[i].id] = getsum(q[i].r) - getsum(q[i].l -1);
  73. }
  74. printf( "Case %d:\n",cas++);
  75. for( int i = 1; i <= m; i++)
  76. printf( "%d\n",ans[i]);
  77. }
  78. return 0;
  79. }

2 B - Sequence II

枚举C点,那么[1 , c-1]区间内,满足i<j&&a[i]<a[j] 的个数设为x;  [c,n]区间内比c大的个数设为用;根据乘法原理,x*y就是所要的,那么枚举所有的C点,累加所有的x*y;


  
  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <string>
  4. #include <string.h>
  5. #include <algorithm>
  6. const int N= 1e5+ 100;
  7. using namespace std;
  8. typedef long long ll;
  9. int c[N],n; //树状数组
  10. int f[N],g[N];
  11. int a[N];
  12. int lowbit(int x)
  13. {
  14. return x&-x;
  15. }
  16. void update(int x)
  17. {
  18. while(x<=n)
  19. {
  20. c[x]+= 1;
  21. x+= lowbit(x);
  22. }
  23. }
  24. int getsum(int x)
  25. {
  26. int ans= 0;
  27. while(x> 0)
  28. {
  29. ans+=c[x];
  30. x-= lowbit(x);
  31. }
  32. return ans;
  33. }
  34. int main()
  35. {
  36. int T;
  37. scanf( "%d",&T);
  38. while(T--)
  39. {
  40. scanf( "%d",&n);
  41. for( int i= 1;i<=n;i++) scanf( "%d",&a[i]);
  42. memset(c, 0, sizeof(c));
  43. for( int i= 1;i<=n;i++)
  44. {
  45. f[i]= getsum(a[i]);
  46. update(a[i]);
  47. }
  48. memset(c, 0, sizeof(c));
  49. for( int i=n;i>= 1;i--)
  50. {
  51. update(a[i]);
  52. g[i]=n-i+ 1- getsum(a[i]);
  53. }
  54. ll ans= 0,sum= 0;
  55. for( int i= 1;i<=n;i++)
  56. {
  57. ans+=sum*g[i];
  58. sum+=f[i];
  59. }
  60. printf( "%I64d\n",ans);
  61. }
  62. return 0;
  63. }

3 C - To the moon

涉及历史查询,一眼主席树的题目,关键的如何进行区间修改。

主席树与线段树不同之处就是  每一棵树是不能修改的,也就是不能够pushDown。那么能否一直新建节点?答案是不可以,如果用新建节点来替代pushDown操作,那么每一次都会一直新建到叶子节点,空间肯定是撑不住的。

主席树区间修一个很巧妙的方法:更新的时候和普通线段树一样,pushup 时由于没有pushDown 子树是不会更新的,所以用自己的 lazy 更新就可以了.也就是 sum[i] = sum[lson[i]] + sum[rson[i]] + lazy[i] * (r - l + 1)。查询的时候,将每一个节点的 lazy 值放在函数参数上带着,解决了pushDown的问题。


  
  1. #include<bits/stdc++.h>
  2. #define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
  3. #define pii pair<int,int>
  4. #define clr(a,b) memset((a),b,sizeof(a))
  5. #define rep(i,a,b) for(int i = a;i < b;i ++)
  6. #define pb push_back
  7. #define MP make_pair
  8. #define LL long long
  9. #define ull unsigned LL
  10. #define ls i << 1
  11. #define rs (i << 1) + 1
  12. #define fi first
  13. #define se second
  14. #define ptch putchar
  15. #define CLR(a) while(!(a).empty()) a.pop()
  16. using namespace std;
  17. inline LL read() {
  18. LL s = 0,w = 1;
  19. char ch = getchar();
  20. while(! isdigit(ch)) {
  21. if(ch == '-') w = -1;
  22. ch = getchar();
  23. }
  24. while( isdigit(ch))
  25. s = s * 10 + ch - '0',ch = getchar();
  26. return s * w;
  27. }
  28. inline void write(LL x) {
  29. if(x < 0)
  30. putchar( '-'), x = -x;
  31. if(x > 9)
  32. write(x / 10);
  33. putchar(x % 10 + '0');
  34. }
  35. const int maxn = 1e5 + 10;
  36. const int M = maxn * 40;
  37. LL C[M],lazy[M];
  38. int lson[M],rson[M];
  39. int tot,T[maxn];
  40. void build(int &i,int l,int r){
  41. i = ++ tot;
  42. C[i] = lazy[i] = 0;
  43. if(l == r){
  44. LL x = read();
  45. C[i] = x;
  46. return ;
  47. }
  48. int mid = (l + r) >> 1;
  49. build(lson[i],l,mid);
  50. build(rson[i],mid + 1,r);
  51. C[i] = C[lson[i]] + C[rson[i]];
  52. }
  53. void update(int l,int r,int ul,int ur,int &i,int last,LL val){
  54. C[++ tot] = C[last];
  55. lson[tot] = lson[last];
  56. rson[tot] = rson[last];
  57. lazy[tot] = lazy[last];
  58. i = tot;
  59. if(ul <= l && r <= ur){
  60. C[i] += (r - l + 1) * val;
  61. lazy[i] += val;
  62. return ;
  63. }
  64. int mid = (l + r) >> 1;
  65. if(ul <= mid) update(l,mid,ul,ur,lson[i],lson[last],val);
  66. if(ur > mid) update(mid + 1,r,ul,ur,rson[i],rson[last],val);
  67. C[i] = C[lson[i]] + C[rson[i]] + (r - l + 1) * lazy[i];
  68. }
  69. LL query(int l,int r,int ql,int qr,int i,LL add){
  70. if(ql <= l && r <= qr){
  71. return C[i] + add * (r - l + 1);
  72. }
  73. int mid = (l + r) >> 1;
  74. LL ans = 0;
  75. if(ql <= mid) ans += query(l,mid,ql,qr,lson[i],add + lazy[i]);
  76. if(qr > mid) ans += query(mid + 1,r,ql,qr,rson[i],add + lazy[i]);
  77. return ans;
  78. }
  79. int main() {
  80. int n = read(),m = read();
  81. build(T[ 0], 1,n);
  82. int now = 0;
  83. while(m --){
  84. char op; scanf( " %c",&op);
  85. if(op == 'C'){
  86. int l = read(),r = read(),d = read();
  87. update( 1,n,l,r,T[now + 1],T[now],d);
  88. ++ now;
  89. }
  90. else if(op == 'Q'){
  91. int l = read(),r = read();
  92. write( query( 1,n,l,r,T[now],lazy[now])); ptch( '\n');
  93. }
  94. else if(op == 'H'){
  95. int l = read(),r = read(),t = read();
  96. write( query( 1,n,l,r,T[t],lazy[t])); ptch( '\n');
  97. }
  98. else {
  99. int t = read();
  100. now = t;
  101. }
  102. }
  103. return 0;
  104. }

4 D - 粟粟的书架

这题其实是分两个题来做

对于R > 1时,设计状态如下:

fi,j,k:从(1,1)到(i,j)的矩形里,大于等于k的数字的总和

gi,j,k:从(1,1)到(i,j)的矩形里,大于等于k的数字的个数

每次询问等价成4个矩形里的结果相加减,二分k,暴力算最小的那个数要多少个就行

对于R == 1时,建立一棵主席树就行了
 


  
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<vector>
  5. #include<bitset>
  6. #include<algorithm>
  7. #include<cstring>
  8. #include<map>
  9. #include<stack>
  10. #include<set>
  11. #include<cmath>
  12. #include<ext/pb_ds/priority_queue.hpp>
  13. using namespace std;
  14. const int maxn = 5E5 + 50;
  15. const int T = 20;
  16. const int N = 202;
  17. const int M = 1010;
  18. int R,C,q,cnt,f[N][N][M],g[N][N][M],root[maxn]
  19. ,lc[maxn*T],rc[maxn*T],c[maxn*T],t[maxn*T];
  20. bool Judge(int r1,int c1,int r2,int c2,int k,int h)
  21. {
  22. return f[r2][c2][k] - f[r1 -1][c2][k] - f[r2][c1 -1][k] + f[r1 -1][c1 -1][k] >= h;
  23. }
  24. void Print(int r1,int c1,int r2,int c2,int k,int h)
  25. {
  26. int z = k + 1;
  27. int ans = g[r2][c2][z] - g[r1 -1][c2][z] - g[r2][c1 -1][z] + g[r1 -1][c1 -1][z];
  28. int res = f[r2][c2][z] - f[r1 -1][c2][z] - f[r2][c1 -1][z] + f[r1 -1][c1 -1][z];
  29. res = h - res;
  30. int Add = res / k;
  31. if (res % k != 0) ++Add;
  32. printf( "%d\n",ans + Add);
  33. }
  34. int Insert(int o,int l,int r,int k)
  35. {
  36. int ret = ++cnt;
  37. c[ret] = c[o] + k;
  38. t[ret] = t[o] + 1;
  39. if (l == r) return ret;
  40. int mid = (l + r) >> 1;
  41. if (k <= mid) rc[ret] = rc[o],lc[ret] = Insert(lc[o],l,mid,k);
  42. else lc[ret] = lc[o],rc[ret] = Insert(rc[o],mid+ 1,r,k);
  43. return ret;
  44. }
  45. int Query(int o1,int o2,int l,int r,int h)
  46. {
  47. int tot = c[o2] - c[o1];
  48. if (tot < h) return -1;
  49. if (l == r) {
  50. int ret = h / l;
  51. if (h % l != 0) ++ret;
  52. return ret;
  53. }
  54. int ret,res,mid = (l + r) >> 1;
  55. res = c[rc[o2]] - c[rc[o1]];
  56. if (res >= h) return Query(rc[o1],rc[o2],mid+ 1,r,h);
  57. ret = t[rc[o2]] - t[rc[o1]];
  58. return ret + Query(lc[o1],lc[o2],l,mid,h - res);
  59. }
  60. int getint()
  61. {
  62. char ch = getchar();
  63. int ret = 0;
  64. while (ch < '0' || '9' < ch) ch = getchar();
  65. while ( '0' <= ch && ch <= '9')
  66. ret = ret* 10 + ch - '0',ch = getchar();
  67. return ret;
  68. }
  69. int main()
  70. {
  71. #ifdef DMC
  72. freopen( "DMC.txt", "r",stdin);
  73. #endif
  74. R = getint();
  75. C = getint();
  76. q = getint();
  77. if (R > 1) {
  78. for ( int i = 1; i <= R; i++)
  79. for ( int j = 1; j <= C; j++) {
  80. int x = getint();
  81. for ( int k = 1; k <= x; k++) {
  82. f[i][j][k] = f[i -1][j][k] + f[i][j -1][k] - f[i -1][j -1][k] + x;
  83. g[i][j][k] = g[i -1][j][k] + g[i][j -1][k] - g[i -1][j -1][k] + 1;
  84. }
  85. for ( int k = x + 1; k <= 1000; k++) {
  86. f[i][j][k] = f[i -1][j][k] + f[i][j -1][k] - f[i -1][j -1][k];
  87. g[i][j][k] = g[i -1][j][k] + g[i][j -1][k] - g[i -1][j -1][k];
  88. }
  89. }
  90. while (q--) {
  91. int r1 = getint(),c1 = getint();
  92. int r2 = getint(),c2 = getint();
  93. int h = getint();
  94. int l = 0,r = 1001;
  95. while (r - l > 1) {
  96. int mid = (l + r) >> 1;
  97. if ( Judge(r1,c1,r2,c2,mid,h)) l = mid;
  98. else r = mid;
  99. }
  100. if ( Judge(r1,c1,r2,c2,r,h)) Print(r1,c1,r2,c2,r,h);
  101. else if ( Judge(r1,c1,r2,c2,l,h)) Print(r1,c1,r2,c2,l,h);
  102. else puts( "Poor QLW");
  103. }
  104. }
  105. else {
  106. for ( int i = 1; i <= C; i++) {
  107. int x = getint();
  108. root[i] = Insert(root[i -1], 1, 1000,x);
  109. }
  110. while (q--) {
  111. int r1 = getint(),c1 = getint();
  112. int r2 = getint(),c2 = getint();
  113. int h = getint();
  114. int ans = Query(root[c1 -1],root[c2], 1, 1000,h);
  115. if (ans != -1) printf( "%d\n",ans);
  116. else puts( "Poor QLW");
  117. }
  118. }
  119. return 0;
  120. }

5 E - 森林

动态维护树上两点间第 k 值 LCT 不能直接维护第 k 值,也很难嵌套其他数据结构,所以我们不考虑它;发现只有 加边没有删边,考虑使用主席树启发式合并 具体来说, 加边时, 把节点少的主席树合并到节点多的主席树上面,复杂度 slogs,s 为 小主席树的大小, 同时动态更新父节点的倍增数组,复杂度也是 slogs 的 查询时, 进入两个点以及它们的 lca 和 lca 的爹的主席树中上上下下加加减减


  
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. #define ls ch[now][0]
  5. #define rs ch[now][1]
  6. #define ols ch[las][0]
  7. #define ors ch[las][1]
  8. const int N= 3e5+ 10;
  9. int ch[N*
  10. 30][ 2],sum[N*
  11. 30],tot;
  12. int f[N][ 20],dep[N],n,m,n_,t,tmp,siz[N],root[N],a[N],b[N],testcase;
  13. void updata(int now){sum[now]=sum[ls]+sum[rs];}
  14. int rebuild(int las,int l,int r,int pos)
  15. {
  16. int now=++tot;
  17. if(l==r) {sum[now]+=sum[las]+ 1; return now;}
  18. int mid=l+r>> 1;
  19. if(pos<=mid)
  20. {
  21. ls= rebuild(ols,l,mid,pos);
  22. rs=ors;
  23. }
  24. else
  25. {
  26. ls=ols;
  27. rs= rebuild(ors,mid+ 1,r,pos);
  28. }
  29. updata(now);
  30. return now;
  31. }
  32. int head[N],to[N<< 1],Next[N<< 1],cnt;
  33. void add(int u,int v)
  34. {
  35. to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
  36. }
  37. int dfs(int now,int fa)
  38. 12
  39. {
  40. int s= 1;
  41. root[now]= rebuild(root[fa], 1,n,b[now]);
  42. f[now][ 0]=fa;
  43. dep[now]=dep[fa]+ 1;
  44. for( int i= 1;i<= 18;i++)
  45. f[now][i]=f[f[now][i− 1]][i− 1];
  46. for( int i=head[now];i;i=Next[i])
  47. if(to[i]!=fa)
  48. s+= dfs(to[i],now);
  49. return s;
  50. }
  51. void swap(int &x,int &y){tmp=x,x=y,y=tmp;}
  52. int rt(int now)
  53. {
  54. for( int i= 18;~i;i−−)
  55. if(f[now][i])
  56. now=f[now][i];
  57. return now;
  58. }
  59. void Merge(int x,int y)
  60. {
  61. int rx= rt(x),ry= rt(y);
  62. if(siz[rx]<siz[ry]) swap(rx,ry), swap(x,y);
  63. add(x,y), add(y,x);
  64. siz[rx]+= dfs(y,x);
  65. }
  66. int LCA(int x,int y)
  67. {
  68. if(dep[x]<dep[y]) swap(x,y);
  69. for( int i= 18;~i;i−−)
  70. if(dep[f[x][i]]>=dep[y])
  71. x=f[x][i];
  72. if(x==y) return x;
  73. for( int i= 18;~i;i−−)
  74. if(f[x][i]!=f[y][i])
  75. x=f[x][i],y=f[y][i];
  76. return f[x][ 0];
  77. }
  78. int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int k)
  79. {
  80. if(l==r) return a[l];
  81. int mid=l+r>> 1,s=sum[ch[rt1][ 0]]+sum[ch[rt2][ 0]]−sum[ch[rt3][ 0]]−sum[ch[rt4
  82. ][ 0]];
  83. if(s>=k) return query(ch[rt1][ 0],ch[rt2][ 0],ch[rt3][ 0],ch[rt4][ 0],l,mid,k);
  84. else return query(ch[rt1][ 1],ch[rt2][ 1],ch[rt3][ 1],ch[rt4][ 1],mid+ 1,r,k−s);
  85. }
  86. int main()
  87. {
  88. scanf( "%d",&testcase);
  89. tot= 0,cnt= 0;
  90. scanf( "%d%d%d",&n_,&m,&t);
  91. for( int i= 1;i<=n_;i++) scanf( "%d",a+i),b[i]=a[i];
  92. std:: sort(a+ 1,a+ 1+n_);
  93. n=std:: unique(a+ 1,a+ 1+n_)−a− 1;
  94. for( int i= 1;i<=n_;i++) b[i]=std:: lower_bound(a+ 1,a+ 1+n,b[i])−a;
  95. for( int u,v,i= 1;i<=m;i++)
  96. {
  97. scanf( "%d%d",&u,&v);
  98. add(u,v), add(v,u);
  99. }
  100. for( int i= 1;i<=n_;i++)
  101. if(!f[i][ 0])
  102. siz[i]= dfs(i, 0);
  103. int lastans= 0; char op[ 4];
  104. for( int u,v,k,i= 1;i<=t;i++)
  105. {
  106. scanf( "%s%d%d",op,&u,&v);
  107. u^=lastans,v^=lastans;
  108. if(op[ 0]== 'Q')
  109. {
  110. scanf( "%d",&k);
  111. k^=lastans; int lca= LCA(u,v);
  112. printf( "%d\n",lastans= query(root[u],root[v],root[lca],root[f[lca][ 0]], 1,n,
  113. k));
  114. }
  115. else
  116. Merge(u,v);
  117. }
  118. return 0;
  119. }


转载:https://blog.csdn.net/m0_61735576/article/details/128553874
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场