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 }

 

posted @ 2020-03-13 16:23  古比  阅读(157)  评论(0编辑  收藏  举报