[关闭]
@Asuna 2017-03-29T12:51:32.000000Z 字数 8329 阅读 726

2016寒假集训队第二次作业

题解


Problem A:敌兵布阵

题意:一个队列,要求满足能单点加减和区间和查询。

题解:裸线段树,update改变单点值即可。

参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. using namespace std;
  7. struct node
  8. {
  9. int l,r,sum,mid;
  10. }f[1000005];
  11. int a[100000],ans,t,n,d=1,x,y;
  12. char s[16];
  13. void uproot(int n)
  14. {
  15. f[n].sum=f[2*n].sum+f[2*n+1].sum;
  16. }
  17. void built(int l,int r,int n)
  18. {
  19. f[n].mid=(l+r)/2;
  20. f[n].r=r;
  21. f[n].l=l;
  22. f[n].sum=0;
  23. if (l==r)
  24. {
  25. f[n].sum=a[l];
  26. return ;
  27. }
  28. built(l,f[n].mid,2*n);
  29. built(f[n].mid+1,r,2*n+1);
  30. uproot(n);
  31. }
  32. void update(int c,int q,int val)
  33. {
  34. if (f[c].l==q && f[c].r==q)
  35. {
  36. f[c].sum+=val;
  37. return ;
  38. }
  39. if (q<=f[c].mid) update(2*c,q,val);
  40. else update(2*c+1,q,val);
  41. uproot(c);
  42. }
  43. void query(int c,int a,int b)
  44. {
  45. //cout<<c<<endl;
  46. // cout<<f[c].l<<" "<<f[c].r<<endl;
  47. // cout<<" "<<a<<" "<<b<<endl;
  48. if (f[c].l>=a && f[c].r<=b)
  49. {
  50. // cout<<"!!!"<<endl;
  51. ans+=f[c].sum;
  52. //cout<<ans<<endl;
  53. return ;
  54. //cout<<"?????"<<endl;
  55. }
  56. if (b<=f[c].mid) query(2*c,a,b);
  57. else if (a>f[c].mid) query(2*c+1,a,b);
  58. else
  59. {
  60. query(2*c,a,f[c].mid);
  61. query(2*c+1,f[c].mid+1,b);
  62. }
  63. }
  64. int main()
  65. {
  66. ios::sync_with_stdio(false);
  67. cin>>t;
  68. while (t--)
  69. {
  70. cin>>n;
  71. for (int i=1; i<=n; i++)
  72. cin>>a[i];
  73. built(1,n,1);
  74. printf("Case %d:\n",d++);
  75. //cout<<"Case "<<d++<<":"<<endl;
  76. while (cin>>s)
  77. {
  78. //cout<<s<<endl;
  79. if (s[0]=='E') break;
  80. cin>>x>>y;
  81. ans=0;
  82. if (s[0]=='A') update(1,x,y);
  83. else if(s[0]=='S') update(1,x,-y);
  84. else if(s[0]=='Q')
  85. {
  86. query(1,x,y);
  87. //cout<<ans<<endl;
  88. printf("%d\n",ans);
  89. }
  90. }
  91. }
  92. return 0;
  93. }

Problem B:Color the ball

题意:一个队列,要求完成区间+1和单点查询。

题解:可以用线段树的段更新点询问,每次把a,b全部+1,最后全部查询一遍就行了。

参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. struct node
  5. {
  6. int l,r,cnt;
  7. }p[1000010];
  8. int mid,n,a,b;
  9. void build(int l,int r,int rt)
  10. {
  11. p[rt].l=l;
  12. p[rt].r=r;
  13. p[rt].cnt=0;
  14. if (l==r) return;
  15. int mid=(l+r)/2;
  16. build(l,mid,rt*2);
  17. build(mid+1,r,rt*2+1);
  18. }
  19. void update(int l,int r,int rt)
  20. {
  21. if (l==p[rt].l && r==p[rt].r)
  22. {
  23. p[rt].cnt++;
  24. return;
  25. }
  26. int mid=(p[rt].l+p[rt].r)/2;
  27. if (r<=mid) update(l,r,rt*2);
  28. else if (l>mid) update(l,r,rt*2+1);
  29. else
  30. {
  31. update(l,mid,rt*2);
  32. update(mid+1,r,rt*2+1);
  33. }
  34. }
  35. int query(int i,int rt)
  36. {
  37. if (p[rt].l==p[rt].r) return p[rt].cnt;
  38. if (p[rt].cnt)
  39. {
  40. p[rt*2].cnt+=p[rt].cnt;
  41. p[rt*2+1].cnt+=p[rt].cnt;
  42. p[rt].cnt=0;
  43. }
  44. mid=(p[rt].l+p[rt].r)/2;
  45. if (i<=mid)
  46. return query(i,rt*2);
  47. else
  48. return query(i,rt*2+1);
  49. }
  50. int main()
  51. {
  52. while (cin>>n)
  53. {
  54. if (n==0) break;
  55. build(1,n,1);
  56. for (int i=1; i<=n; i++)
  57. {
  58. cin>>a>>b;
  59. update(a,b,1);
  60. }
  61. for (int i=1; i<n; i++)
  62. cout<<query(i,1)<<" ";
  63. cout<<query(n,1)<<endl;
  64. }
  65. return 0;
  66. }

Problem C:Mayor's posters

题意:有n张海报,按给出的顺序依次贴在一面墙上,第i张海报的左右端点为li和ri,问最终能看到几张不同的海报。

题解:由于li和ri的范围太大(大的超出想象),于是看了js的题解之后才知道可以离散化解决,也就是通过压缩没数据的空出空间来缩小li和ri的范围,比如[1,2],[2,3],[1,5],[4,9],离散化就是先记录每个端点对应的区间,然后对每个端点排序,排序后为1,1,2,2,3,4,5,9。之后从1~9进行压缩,这里1,2,3,4,5都有数,因此无法压缩,只有9可以压缩成6,然后放回原先区间则离散化后的区间为[1,2],[2,3],[1,5],[4,6]。在离散化之后只需要用线段树从后往前贴,查询到某块区域被完全覆盖了就返回即可。

参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. int flag,n,t,ans;
  7. struct node
  8. {
  9. int l,r;
  10. bool vis;//判断是不是完全覆盖
  11. }tree[400010];
  12. struct point
  13. {
  14. int id,x;
  15. }a[400010];
  16. int cmp1(point a,point b)
  17. {
  18. return a.x<b.x;
  19. }
  20. int cmp2(point a,point b)
  21. {
  22. if (a.id==b.id)
  23. return a.x<b.x;
  24. return a.id>b.id;
  25. }
  26. void PushUp(int rt)
  27. {
  28. tree[rt].vis=tree[(rt*2)].vis && tree[(rt*2+1)].vis;
  29. }
  30. void build(int l,int r,int rt)
  31. {
  32. tree[rt].l=l;
  33. tree[rt].r=r;
  34. tree[rt].vis=0;
  35. if (tree[rt].l==tree[rt].r) return;
  36. int mid=(l+r)/2;
  37. build(l,mid,(rt*2));
  38. build(mid+1,r,(rt*2+1));
  39. }
  40. void query(int l,int r,int rt)
  41. {
  42. if (tree[rt].vis) return ;
  43. if (tree[rt].l==l && tree[rt].r==r)
  44. {
  45. tree[rt].vis=1;
  46. flag=1;
  47. return ;
  48. }
  49. int mid=(tree[rt].l+tree[rt].r)/2;
  50. if (r<=mid)
  51. query(l,r,(rt*2));
  52. else
  53. if (l>=mid+1)
  54. query(l,r,(rt*2+1));
  55. else
  56. {
  57. query(l,mid,(rt*2));
  58. query(mid+1,r,(rt*2+1));
  59. }
  60. PushUp(rt);
  61. }
  62. int main()
  63. {
  64. cin>>t;
  65. while (t--)
  66. {
  67. cin>>n;
  68. for (int i=0; i<2*n; i+=2)
  69. {
  70. scanf("%d%d",&a[i].x,&a[i+1].x);
  71. a[i].id=a[i+1].id=i;
  72. }
  73. sort(a,a+2*n,cmp1);
  74. int tot=0,pre=0;
  75. //离散
  76. for(int i=0; i<2*n; i++)
  77. {
  78. if (a[i].x==pre) a[i].x=tot;
  79. else
  80. {
  81. pre=a[i].x;
  82. tot++;
  83. a[i].x=tot;
  84. }
  85. }
  86. build(1,2*n,1);
  87. sort(a,a+2*n,cmp2); //排序,从后往前贴
  88. ans=0;
  89. for(int i=0;i<2*n;i+=2)
  90. {
  91. int l=a[i].x,r=a[i+1].x;
  92. flag=0;
  93. query(l,r,1);
  94. if (flag) ans++;
  95. }
  96. cout<<ans<<endl;
  97. }
  98. return 0;
  99. }

Problem D:Tunnel Warfare

题意:一条线上有n个点从1~n。
D x是破坏x这个点。
Q x是表示查询以x所在的最长的连续的点的个数
R是恢复上一次破坏的点。

题解:设置一个lc记录区间左端点开始的最大连续个数,rc 记录区间右端点开始的最大的连续个数,sc表示该区间最大的连续点的个数。多维护一个栈来保存被破坏的点,以便于之后可能的修复。每次查询分别往左右子树找最大连续个数最后用max(lc,rc)来更新sc。

参考代码:

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. #include<stack>
  6. using namespace std;
  7. int lc[400005],rc[400005],sc[400005],n,m,x;
  8. char c;
  9. stack<int> a;
  10. void uppush(int s,int m)
  11. {
  12. lc[s]=lc[s*2];
  13. rc[s]=rc[s*2+1];
  14. if (lc[s]==m-(m/2)) lc[s]+=lc[s*2+1];
  15. if (rc[s]==m/2) rc[s]+=rc[s*2];
  16. sc[s]=max(lc[s*2+1]+rc[s*2],max(sc[s*2],sc[s*2+1]));
  17. }
  18. void build(int l,int r,int s)
  19. {
  20. lc[s]=rc[s]=sc[s]=r-l+1;
  21. if (l==r) return ;
  22. int mid=(l+r)/2;
  23. build(l,mid,s*2);
  24. build(mid+1,r,s*2+1);
  25. }
  26. void update(int p,int c,int l,int r,int s)
  27. {
  28. if (l==r)
  29. {
  30. if (c)
  31. {
  32. lc[s]=rc[s]=sc[s]=1;
  33. return ;
  34. }
  35. else
  36. {
  37. lc[s]=rc[s]=sc[s]=0;
  38. return ;
  39. }
  40. }
  41. int mid=(l+r)/2;
  42. if (p<=mid) update(p,c,l,mid,s*2);
  43. else update(p,c,mid+1,r,s*2+1);
  44. uppush(s,r-l+1);
  45. }
  46. int query(int p,int l,int r,int s)
  47. {
  48. if (l==r || sc[s]==0 || sc[s]==r-l+1)
  49. return sc[s];
  50. int mid=(l+r)/2;
  51. if (p<=mid)
  52. {
  53. if (p>=mid-rc[s*2]+1)
  54. return rc[s*2]+query(mid+1,mid+1,r,s*2+1);
  55. else
  56. return query(p,l,mid,s*2);
  57. }
  58. else
  59. {
  60. if (p<=mid+lc[s*2+1])
  61. return lc[s*2+1]+query(mid,l,mid,s*2);
  62. else return query(p,mid+1,r,s*2+1);
  63. }
  64. }
  65. int main()
  66. {
  67. while(cin>>n>>m)
  68. {
  69. build(1,n,1);
  70. while (m--)
  71. {
  72. //getchar();
  73. cin>>c;
  74. if (c!='R')
  75. cin>>x;
  76. if (c=='D')
  77. {
  78. update(x,0,1,n,1);
  79. a.push(x);
  80. }
  81. if (c=='R')
  82. {
  83. update(a.top(),1,1,n,1);
  84. a.pop();
  85. }
  86. if (c=='Q') cout<<query(x,1,n,1)<<endl;
  87. }
  88. }
  89. return 0;
  90. }

Problem E:A Simple Problem with Integers

题意:给定一个数列,有如下两个操作:
(1)C a b c,表示第a个到第b个数全部加上c
(2)Q a b,表示询问第a个数到b个数的和

题解:普通的区间修改和区间和询问。注意范围可能比int大,要用long long统计结果。

参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. int num[100005],t,n,x,y,z;
  7. long long sum[400005],a[400005];
  8. char c[2];
  9. void uproot(int s)
  10. {
  11. sum[s]=sum[s*2]+sum[s*2+1];
  12. }
  13. void pushdown(int s,int l)
  14. {
  15. if (a[s])
  16. {
  17. a[s*2]+=a[s];
  18. a[s*2+1]+=a[s];
  19. sum[s*2]+=a[s]*(l-(l/2));
  20. sum[s*2+1]+=a[s]*(l/2);
  21. a[s]=0;
  22. }
  23. }
  24. void build(int s,int l,int r)
  25. {
  26. if (l==r)
  27. {
  28. cin>>sum[s];
  29. return ;
  30. }
  31. int mid=(l+r)/2;
  32. build(s*2,l,mid);
  33. build(s*2+1,mid+1,r);
  34. uproot(s);
  35. }
  36. void update(int c,int x,int y,int l,int r,int s)
  37. {
  38. if (l>=x && r<=y)
  39. {
  40. a[s]+=c;
  41. sum[s]+=c*(r-l+1);
  42. return ;
  43. }
  44. pushdown(s,r-l+1);
  45. int mid=(l+r)/2;
  46. if (x<=mid)
  47. update(c,x,y,l,mid,s*2);
  48. if (y>mid)
  49. update(c,x,y,mid+1,r,s*2+1);
  50. uproot(s);
  51. }
  52. long long query(int x,int y,int l,int r,int s)
  53. {
  54. if (l>=x && r<=y)
  55. return sum[s];
  56. pushdown(s,r-l+1);
  57. int mid=(l+r)>>1;
  58. long long ans=0;
  59. if(x<=mid)
  60. ans+=query(x,y,l,mid,s*2);
  61. if(y>mid)
  62. ans+=query(x,y,mid+1,r,s*2+1);
  63. return ans;
  64. }
  65. int main()
  66. {
  67. std::ios::sync_with_stdio(false);
  68. cin>>n>>t;
  69. build(1,1,n);
  70. while(t--)
  71. {
  72. cin>>c;
  73. if (c[0]=='C')
  74. {
  75. cin>>x>>y>>z;
  76. update(z,x,y,1,n,1);
  77. }
  78. else
  79. {
  80. cin>>x>>y;
  81. cout<<query(x,y,1,n,1)<<endl;
  82. }
  83. }
  84. return 0;
  85. }

Problem F:Sasha and Array

题意:给定一个数列,有如下两种操作:
(1)1 l r x,表示第l个到第r个数全部加上x
(2)2 l r,表示询问第l个数到r个数对应斐波那契的第ai项的和,输出mod10^9+7。

题解:呃啊。。寒假的时候看到这题巨烦。。线段树+斐波那契简直恶心就没做。。现在想来应该是线段树+矩阵快速幂(别问我为什么想到矩阵。。因为它是斐波那契。。)总的来说就是维护一个经典的斐波那契矩阵(2*2的(1,0,0,1))。于是用线段树存储矩阵,每次更新加x就可以用乘以(1,0,0,1)^x代替

参考代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cstdlib>
  5. #include<cmath>
  6. #include<algorithm>
  7. using namespace std;
  8. const long long mod=1e9+7;
  9. int n,m,op,a,b;
  10. long long x;
  11. struct Mat
  12. {
  13. long long x[2][2];
  14. void init()
  15. {
  16. x[0][0]=x[1][1]=1;
  17. x[1][0]=x[0][1]=0;
  18. }
  19. Mat operator*(const Mat& m2)const
  20. {
  21. Mat m;
  22. m.x[0][0]=m.x[0][1]=m.x[1][0]=m.x[1][1]=0;
  23. for(int k=0;k<2;k++)
  24. for(int i=0;i<2;i++)
  25. for(int j=0;j<2;j++)
  26. m.x[i][j]=(m.x[i][j]+x[i][k]*m2.x[k][j])%mod;
  27. return m;
  28. }
  29. Mat operator+(const Mat& m2)const{
  30. Mat m;
  31. for(int i=0;i<2;i++)
  32. for(int j=0;j<2;j++)
  33. m.x[i][j]=(x[i][j]+m2.x[i][j])%mod;
  34. return m;
  35. }
  36. };
  37. Mat sum[400010],add[400010];
  38. Mat pow(long long n)
  39. {
  40. Mat m,ret;
  41. m.x[0][0]=1;m.x[0][1]=1;
  42. m.x[1][0]=1;m.x[1][1]=0;
  43. ret.init();
  44. while(n)
  45. {
  46. if (n&1) ret=ret*m;
  47. m=m*m;
  48. n>>=1;
  49. }
  50. return ret;
  51. }
  52. long long solve(long long n)
  53. {
  54. Mat ret=pow(n);
  55. return ret.x[0][0];
  56. }
  57. void uppush(int rt)
  58. {
  59. sum[rt]=sum[rt*2]+sum[rt*2+1];
  60. }
  61. void build(int l,int r,int rt)
  62. {
  63. sum[rt].init();
  64. add[rt].init();
  65. if(l==r)
  66. {
  67. long long x;
  68. cin>>x;
  69. sum[rt]=pow(x-1);
  70. return;
  71. }
  72. int m=(l+r)/2;
  73. build(l,m,rt*2);
  74. build(m+1,r,rt*2+1);
  75. uppush(rt);
  76. }
  77. void pushdown(int rt)
  78. {
  79. sum[rt*2]=sum[rt*2]*add[rt];
  80. sum[rt*2+1]=sum[rt*2+1]*add[rt];
  81. add[rt*2]=add[rt*2]*add[rt];
  82. add[rt*2+1]=add[rt*2+1]*add[rt];
  83. add[rt].init();
  84. }
  85. void update(int L,int R,Mat c,int l,int r,int rt)
  86. {
  87. if (L<=l && R>=r)
  88. {
  89. sum[rt]=sum[rt]*c;
  90. add[rt]=add[rt]*c;
  91. return;
  92. }
  93. pushdown(rt);
  94. int m=(l+r)/2;
  95. if(L<=m) update(L,R,c,l,m,rt*2);
  96. if(R>m) update(L,R,c,m+1,r,rt*2+1);
  97. uppush(rt);
  98. }
  99. long long query(int L,int R,int l,int r,int rt)
  100. {
  101. if (L<=l && R>=r)
  102. {
  103. return sum[rt].x[0][0];
  104. }
  105. pushdown(rt);
  106. int mid=(l+r)/2;
  107. long long ret=0;
  108. if (L<=mid) ret=(ret+query(L,R,l,mid,rt*2))%mod;
  109. if (R>mid) ret=(ret+query(L,R,mid+1,r,rt*2+1))%mod;
  110. uppush(rt);
  111. return ret;
  112. }
  113. int main()
  114. {
  115. while(cin>>n>>m)
  116. {
  117. build(1,n,1);
  118. while(m--)
  119. {
  120. scanf("%d%d%d",&op,&a,&b);
  121. if (op==1)
  122. {
  123. cin>>x;
  124. Mat m=pow(x);
  125. update(a,b,m,1,n,1);
  126. }
  127. else cout<<query(a,b,1,n,1)<<endl;
  128. }
  129. }
  130. return 0;
  131. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注