D 筱玛爱线段树 (线段树)或(差分)
本题题意:
先考虑单纯用线段树来解的做法 复杂度O(2*log(n)*n)
单纯用线段树来解的话,需要用到两颗线段树;
第一颗:处理某操作需要执行多少次;
第二课:将第一颗得到的操作执行;
那么对于第一颗树:
当op==1的时候,就直接在这个位置+1;
当op==2的时候,就查询这个位置已经被操作累加了多少次(其他操作2能够对其进行累加),然后update他的范围;
那么我们就再用一个数组去储存操作1的个数
然后再开一个线段树,来存储最后答案
1 #include <bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int maxn = 1e5 + 5; 5 const int INF = 0x3f3f3f3f; 6 const ll mod = 1e9 + 7; 7 int op[maxn],l[maxn],r[maxn]; 8 ll sum[maxn*4],lazy[maxn*4]; 9 void push(int p){ 10 sum[p]=(sum[p<<1]+sum[p<<1|1])%mod; 11 } 12 void pushdown(int l,int r,int p){ 13 int mid=(l+r)/2; 14 if(lazy[p]){ 15 lazy[p<<1]+=lazy[p]; lazy[p<<1]%=mod; 16 lazy[p<<1|1]+=lazy[p]; lazy[p<<1|1]%=mod; 17 sum[p<<1]+=lazy[p]*(mid-l+1); sum[p<<1]%=mod; 18 sum[p<<1|1]+=lazy[p]*(r-mid); sum[p<<1|1]%=mod; 19 lazy[p]=0; 20 } 21 } 22 void build(int l,int r,int p){ 23 if(l==r){ 24 sum[p]=0; lazy[p]=0; 25 return ; 26 } 27 int mid=(l+r)/2; 28 build(l,mid,p<<1); 29 build(mid+1,r,p<<1|1); 30 push(p); 31 } 32 void update(int l,int r,int L,int R,ll val,int p){ 33 if(L<=l&&r<=R){ 34 lazy[p]+=val; lazy[p]%=mod; 35 sum[p]+=val*(r-l+1); sum[p]%=mod; 36 return ; 37 } 38 pushdown(l,r,p); 39 int mid=(l+r)/2; 40 if(L<=mid) update(l,mid,L,R,val,p<<1); 41 if(R>mid) update(mid+1,r,L,R,val,p<<1|1); 42 push(p); 43 } 44 ll query(int l,int r,int L,int R,int p){ 45 if(L<=l&&r<=R){ 46 return sum[p]%mod; 47 } 48 pushdown(l,r,p); 49 int mid=(l+r)/2; 50 ll ans=0; 51 if(L<=mid) ans+=query(l,mid,L,R,p<<1); 52 if(R>mid) ans+=query(mid+1,r,L,R,p<<1|1); 53 return ans%mod; 54 } 55 ll tp[maxn]; 56 int main(){ 57 int n,m; cin>>n>>m; 58 for(int i=1;i<=m;i++){ 59 cin>>op[i]>>l[i]>>r[i]; 60 } 61 build(1,m,1); 62 for(int i=m;i>=1;i--){ 63 if(op[i]==1) update(1,m,i,i,1,1); 64 else{ 65 ll Q=query(1,m,i,i,1);//从后往前确定操作1的次数 66 update(1,m,l[i],r[i],Q+1,1); 67 } 68 } 69 for(int i=1;i<=m;i++){//每个操作1究竟执行几次 70 tp[i]=query(1,m,i,i,1); 71 } 72 memset(lazy,0,sizeof(lazy)); 73 memset(sum,0,sizeof(sum)); 74 build(1,n,1); 75 for(int i=1;i<=m;i++){ 76 if(op[i]==1){ 77 update(1,n,l[i],r[i],tp[i],1); 78 } 79 } 80 for(int i=1;i<=n;i++){ 81 cout<<query(1,n,i,i,1)<<" "; 82 } 83 cout<<endl; 84 }
本题也可以线段树+差分的做法,这种做法就是将上述代码的第二棵树用差分的方法来表示即可;
现在来介绍第三种做法,二次差分;
也就是说,将上述所说的两棵树都用差分的方法来表示;
那么我们要用两个数组来表示 d 数组用来表示某操作执行多少次 (后缀) b数组这是用来表示最后答案的一个数组(前缀)
我们d数组是用后缀的方式,要求出某一位的操作次数,则是其后缀和
然后操作的时候,假如op==2,则根据差分规则来更新d数组(D数组是后缀和,所以自然是用后缀来写)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int mod=1e9+7; 5 int l[100005],r[100005],op[100005]; 6 ll d[100005],b[100005]; 7 int main(){ 8 int n,m; 9 scanf("%d%d",&n,&m); 10 memset(d,0,sizeof(d)); 11 memset(b,0,sizeof(b)); 12 for(int i=1;i<=m;i++){ 13 scanf("%d%d%d",&op[i],&l[i],&r[i]); 14 } 15 d[m+1]=1; //d数组是后缀的, 16 d[0]--; //因为一开始所有操作数都是1,所以D数组的初始便是这样 17 for(int i=m;i>=1;i--){ 18 d[i]=(d[i+1]+d[i])%mod; //计算出次位置的操作总共执行了多少次 19 if(op[i]==2){ 20 d[l[i]-1]=(d[l[i]-1]-d[i]+mod)%mod; // 因为d数组是后缀来表示的 21 d[r[i]]=(d[r[i]]+d[i])%mod; //所以自然是用反过来 22 } 23 else{ 24 b[l[i]]=(b[l[i]]+d[i])%mod; //b数组依旧是前缀的 25 b[r[i]+1]=(b[r[i]+1]-d[i]+mod)%mod; //所以照旧 26 } 27 } 28 for(int i=1;i<=n;i++){ 29 b[i]=(b[i-1]+b[i])%mod; 30 printf("%lld ",b[i]); 31 } 32 }