当前位置:   article > 正文

一、二维前缀和与差分_二维前缀和差分

二维前缀和差分

一维前缀和:前i项的和

给定长度为n的序列a1,a2...an,则sum[i]=a1+...+ai=sum[i-1]+a[i]

  1. for(i=1;i<=n;i++)
  2. {
  3. cin>>a[i];
  4. a[i]+=a[i-1];
  5. }

一维差分:区间一次修改求和

差分:每个元素与前一个元素的差值

如       A:1 2 3 4 5

则差分为  1 1 1 1 1

若将A[1]+1, A : 1 3 3 4 5

则                      1 2 0 1 1      只会改变自己和后一个数

m次操作,每次将区间[L,R]的值加p,最后询问区间[L,R]元素之和。

solution:用一个数组b记录对每一个前缀和总的修改(也可以就在a上操作)。则每次b[L]+=p,b[R]-=p(代表从L及以后的每个元素都要+p,而R及其以后的每个元素-p(所以R之后的元素相当于没操作))

  1. for(i=1; i<=m; i++)
  2. {
  3. int L,R;
  4. cin>>L>>R>>p;
  5. b[L]+=p;
  6. b[R+1]-=p;
  7. }
  8. int add=0;
  9. for(i=1; i<=n; i++)
  10. {
  11. add+=b[i];
  12. a[i]+=a[i-1]+add;
  13. }
  14. //若直接用a表示则为:
  15. for(i=1; i<=m; i++)
  16. {
  17. int L,R;
  18. cin>>L>>R>>p;
  19. a[L]+=p;
  20. a[R+1]-=p;
  21. }
  22. int add=0;
  23. for(i=1; i<=n; i++)
  24. {
  25. add+=a[i];
  26. a[i]+=a[i-1];
  27. }

要求[L,R]的和即a[R]-a[L-1],注意下标从0开始时要特判L=0

二维前缀和

这时操作对象从一维数组变为二维数组,sum[i][j]表示以a[0][0]为左上角,a[i][j]为右下角的矩阵的和,则可推出

sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i][j] +a[i][j]//容斥

  1. for(i, 1, n)
  2. for(j, 1, n)
  3. {
  4. scanf("%d", &a[i][j]);
  5. a[i][j] +=a[i-1][j] + a[i][j-1] - a[i-1][j-1];
  6. }

二维差分

m次操作,每次将以[x1,y1]为左上角,[x2,y2]为右上角的矩阵的值加p,最后询问区间[L,R]元素之和。

同样地,我们记录下每次操作对区间和的影响。b[i][j]表示以[0][0]为左上角的矩阵,若包含a[i][j]则要+p,所以有4处影响:

b[x1][y1]+=p;

b[x1][y2=1]-=p;(右边区域+、-各一次,抵消使得1号区域不收影响);

b[x2+1][y1]-=p;(下边区域+、-各一次,抵消使得2号区域不收影响);

b[x2+1][y2+1]+=p;(右下边区域+、-各两次,抵消使得3号区域不收影响);

  1. for(i, 1, m)
  2. {
  3. int x1, y1, x2, y2, p;
  4. scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &p);
  5. a[x1][y1] += p;
  6. a[x1][y2+1] -= p;
  7. a[x2+1][y1] -= p;
  8. a[x2+1][y2+1] += p;
  9. }
  10. //注意这样算的是每一个元素更新之后的值,不是和!要求和的话不仅要加a[i][j]还得加上覆盖矩阵的每一个未更新a[i][j],即累加add
  11. for(i, 1, n)
  12. for(j, 1, n)
  13. a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];

则查询以[x1,y1]为左上角,[x2,y2]为右上角的矩阵的值的和:

ans=a[x2][y2]-a[x2,y2-1]-a[x2-1][y2]+a[x1][y1]

sort+树状数组求大矩阵二位前缀和

A. The beautiful values of the palace

theme:给定一个n*n螺旋矩阵,现从螺旋矩阵中选出m个位置,每个位置的价值由它的元素值的每个数位之和表示,其余位置的元素全部置为0,q次询问,每次询问子矩阵价值。1<=n<=1e6,m,q<=1e5

solution:首先要根据螺旋矩阵的性质推出给定x,y,矩阵中(x,y)的元素值。之后问题转化为求二维矩阵的前缀和。n范围很大,所以借助树状数组辅助。

  1. 用一个结构体存每个点,包含成员变量flag:标志操作,0表示为将原数插入树状数组,1表示计算询问前缀和。x,y分别记录在矩阵中的横纵坐标。var:记录求子矩阵是时的正负号,即是加上一个前缀和还是减去。
  2. 将选出的点按y坐标优先,x坐标次之按从小到大排序。
  3. 排序后边查询边插入(树状数组将横坐标作为下标),按y坐标插入,这样算前缀和时算横坐标<=x的即可
  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=(int)1e6+10;
  5. ll bit[N],ans[N];
  6. void Init(int n)
  7. {
  8. for(int i=1;i<=n;++i)
  9. bit[i]=ans[i]=0;
  10. }
  11. /******************树状数组**************/
  12. int lowbit(int x)
  13. {
  14. return x&(-x);
  15. }
  16. void add(int i,ll t)
  17. {
  18. while(i<=N)
  19. {
  20. bit[i]+=t;
  21. i+=lowbit(i);
  22. }
  23. }
  24. ll sum(int i)
  25. {
  26. ll sum=0;
  27. while(i)
  28. {
  29. sum+=bit[i];
  30. i-=lowbit(i);
  31. }
  32. return sum;
  33. }
  34. /******************矩阵**************/
  35. struct node
  36. {
  37. int f;//标志是矩阵原数0插入还是求区间和时的ans+/-,1
  38. int x,y,ans_id;
  39. ll val;//标志求前缀和时是减掉还是加上该矩阵
  40. friend bool operator<(node a,node b)//y,x,f
  41. {
  42. if(a.y==b.y)
  43. {
  44. if(a.x==b.x)
  45. return a.f<b.f;
  46. return a.x<b.x;
  47. }
  48. return a.y<b.y;
  49. }
  50. }p[N];
  51. ll re_val(ll x)
  52. {
  53. ll sum=0;
  54. while(x>0)
  55. {
  56. sum+=x%10;
  57. x/=10;
  58. }
  59. return sum;
  60. }
  61. /************螺旋矩阵*************/
  62. long long index(long long y,long long x,long long n)
  63. {
  64. long long mid=(n+1)/2;
  65. long long p=max(abs(x-mid),abs(y-mid));
  66. long long ans=n*n-(1+p)*p*4;
  67. long long sx=mid+p,sy=mid+p;
  68. if(x==sx&&y==sy)
  69. return ans;
  70. else
  71. {
  72. if(y==sy||x==sx-2*p)
  73. return ans+abs(x-sx)+abs(y-sy);
  74. else
  75. return ans+8*p-abs(x-sx)-abs(y-sy);
  76. }
  77. }
  78. /*************二位前缀和***************/
  79. void solve(int n)
  80. {
  81. for(int i=1;i<=n;++i)
  82. {
  83. if(p[i].f) ans[p[i].ans_id]+=sum(p[i].x)*p[i].val;
  84. else add(p[i].x,p[i].val);
  85. }
  86. }
  87. int main()
  88. {
  89. int T;
  90. scanf("%d",&T);
  91. while(T--)
  92. {
  93. int n,m,ask;
  94. scanf("%d%d%d",&n,&m,&ask);
  95. Init(N-3);
  96. int cnt=0;
  97. for(int i=1;i<=m;++i)
  98. {
  99. int x,y;
  100. scanf("%d%d",&x,&y);
  101. p[++cnt]={0,x,y,-1,re_val(index(x,y,n))};
  102. }
  103. for(int i=1;i<=ask;++i)
  104. {
  105. int xl,yl,xr,yr;
  106. scanf("%d%d%d%d",&xl,&yl,&xr,&yr);
  107. p[++cnt]={1,xl-1,yl-1,i,1};
  108. p[++cnt]={1,xl-1,yr,i,-1};
  109. p[++cnt]={1,xr,yl-1,i,-1};
  110. p[++cnt]={1,xr,yr,i,1};
  111. }
  112. sort(p+1,p+1+cnt);
  113. solve(cnt);
  114. for(int i=1;i<=ask;++i)
  115. printf("%lld\n",ans[i]);
  116. }
  117. return 0;
  118. }

高阶差分

theme:三种操作:

(1):1 x:表示从位置x开始一直到n,每个数+1

(2):  2 x :表示从x到n,依次增加1 2 3 4、、、

(3):3 x:表示从x到n,依次增加1 4 9 16、、、

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define far(i,t,n) for(int i=t;i<n;++i)
  4. typedef long long ll;
  5. typedef unsigned long long ull;
  6. using namespace std;
  7. int a[100010],b[100010],c[100010];
  8. int mod=1e9+7;
  9. void pre(int p[],int n)
  10. {
  11. p[0]%=mod;
  12. far(i,1,n)
  13. p[i]=(p[i]+p[i-1]%mod)%mod;
  14. }
  15. int main()
  16. {
  17. int t;
  18. cin>>t;
  19. while(t--)
  20. {
  21. int n,m;
  22. scanf("%d%d",&n,&m);
  23. memset(a,0,sizeof(a));
  24. memset(b,0,sizeof(b));
  25. memset(c,0,sizeof(c));
  26. far(i,0,m)
  27. {
  28. int op,pos;
  29. scanf("%d%d",&op,&pos);
  30. --pos;
  31. if(op==1)
  32. a[pos]++;
  33. else if(op==2)
  34. b[pos]++;
  35. else
  36. c[pos]++,c[pos+1]++;
  37. }
  38. pre(a,n);
  39. pre(b,n);pre(b,n);
  40. pre(c,n);pre(c,n);pre(c,n);
  41. far(i,0,n)
  42. printf("%d ",(a[i]+b[i]+c[i])%mod);
  43. printf("\n");
  44. }
  45. }

差分套差分:

theme:两种操作: 1 l r:表示将区间[l,r]的数+1,2 l r :表示将编号[l,r]的操作再执行一遍(保证l,r为已出现的编号)。开始时n个数为0,问最终每个数为多少?

solution:最终我们要算出每个1操作执行了多少次,再对这些区间进行差分,一遍前缀和可得到结果。而对操作次数进行差分的时候,应该逆序进行,从后往前,进行差分,两个差分同步进行。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define far(i,t,n) for(int i=t;i<n;++i)
  4. typedef long long ll;
  5. typedef unsigned long long ull;
  6. using namespace std;
  7. map<int,int>m;
  8. map<int,int>::iterator it;
  9. priority_queue<int>q;
  10. int main()
  11. {
  12. int n;
  13. cin>>n;
  14. far(i,0,n)
  15. {
  16. int x;
  17. scanf("%d",&x);
  18. m[x]++;
  19. }
  20. int ans=0;
  21. for(it=m.begin();it!=m.end();++it)
  22. q.push(it->second);
  23. while(q.size()>=3)
  24. {
  25. ++ans;
  26. int x1=q.top()-1;
  27. q.pop();
  28. int x2=q.top()-1;
  29. q.pop();
  30. int x3=q.top()-1;
  31. q.pop();
  32. if(x1)q.push(x1);
  33. if(x2)q.push(x2);
  34. if(x3)q.push(x3);
  35. }
  36. cout<<ans<<"\n";
  37. }
  38. /*
  39. 13
  40. 1 2 3 3 4 4 4 4 5 5 5 5 5
  41. */

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/434009
推荐阅读
相关标签
  

闽ICP备14008679号