让人一看就知道是线段树的题,不过具体操作起来就比较DT了。
我的做法是每个结点保存以这个区间开头的每隔多少个数加上一个数,说白了,就是题目说的第一种操作的k,c,k<=10,所以一个s[11]数组可以解决。
然后每个1 a b k c 操作,就是对区间[a,b]的s[k]加上一个c,若是线段树中存在[a,b]直接加上即可,否则,左孩子仍然照加,右孩子就需要找第一个属于[a,b]区间且i-a%k==0的数,变成给[i,b]的s[k]加上c
计算某个点的值时,需要把涵盖这个点的所有线段上的s数组对它的影响算进去,即看pos-left mod i是否等于0。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=50005; 6 typedef long long ll; 7 struct line 8 { 9 int left,right; 10 ll s[11]; 11 }line[N*3]; 12 void build(int now,int left,int right) 13 { 14 line[now].left=left; 15 line[now].right=right; 16 memset(line[now].s,0,sizeof(line[now].s)); 17 if(left==right) 18 return; 19 int mid=(left+right)>>1; 20 build(now*2,left,mid); 21 build(now*2+1,mid+1,right); 22 } 23 void add(int now,int left,int right,int k,ll c) 24 { 25 if(left>right) 26 return; 27 int ll=line[now].left,rr=line[now].right,mid; 28 if(ll==left&&rr==right) 29 { 30 line[now].s[k]+=c; 31 return; 32 } 33 mid=(ll+rr)>>1; 34 if(left>mid) 35 add(now*2+1,left,right,k,c); 36 else if(right<=mid) 37 add(now*2,left,right,k,c); 38 else 39 { 40 add(now*2,left,mid,k,c); 41 int st=(k-(mid+1-left)%k)%k; 42 add(now*2+1,mid+1+st,right,k,c); 43 } 44 } 45 ll cac(int now,int pos) 46 { 47 ll ans=0; 48 int t=pos-line[now].left; 49 for(int i=1;i<=10;i++) 50 { 51 if(t%i==0) 52 ans+=line[now].s[i]; 53 } 54 int mid=(line[now].left+line[now].right)>>1; 55 if(line[now].left==line[now].right) 56 return ans; 57 if(pos<=mid) 58 return cac(now*2,pos)+ans; 59 else 60 return cac(now*2+1,pos)+ans; 61 } 62 ll ar[N]; 63 int main() 64 { 65 int n,m,op,a,b,k; 66 ll c; 67 while(scanf("%d",&n)!=EOF) 68 { 69 for(int i=1;i<=n;i++) 70 scanf("%I64d",ar+i); 71 build(1,1,n); 72 scanf("%d",&m); 73 while(m--) 74 { 75 scanf("%d%d",&op,&a); 76 if(op==2) 77 { 78 printf("%I64d\n",ar[a]+cac(1,a)); 79 } 80 else 81 { 82 scanf("%d%d%I64d",&b,&k,&c); 83 add(1,a,b,k,c); 84 } 85 } 86 } 87 return 0; 88 }