树状数组/线段树总结

acwing 树状数组 线段树

 

题目链接:https://www.acwing.com/problem/content/245/ ###K

类似于已知逆序对 还原原数组的过程, 从倒序开始还原, 本质是求数组中的第k小值

那么用树状数组的话就需要通过二分来做, 通过query(mid)>=k 来一直找到第一个满足题意的x 

找到后还需要删除操作  注意二分的时候不能 l=mid  因为这样就可能不是找到第一个等于的

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+10;
 4 const int mod=1e9+7;
 5 #define ll long long
 6 #define pi pair<int,int>
 7 #define fi first
 8 #define sc second
 9 #define pb push_back
10 int n;
11 int a[maxn];
12 int tr[maxn];
13 
14 void add(int x,int v)
15 {
16     while(x<=n) tr[x]+=v,x+=x&-x;
17 }
18 int query(int x)
19 {
20     int ans=0;
21     while(x) ans+=tr[x],x-=x&-x;
22     return ans;
23 }
24 
25 int solve(int x)
26 {
27     int l=1,r=n;
28     int ans=0;
29     while(l<=r)
30     {
31         int mid=(l+r)/2;
32         if(query(mid)>=x)
33         {
34             ans=mid;
35             r=mid-1;
36         }
37         else
38             l=mid+1;
39     }
40     return ans;
41 }
42 
43 int main()
44 {
45     ios::sync_with_stdio(0);
46     cin.tie(0);
47     cin>>n;
48     for(int i=1;i<=n;i++) add(i,1);
49     for(int i=2;i<=n;i++) cin>>a[i];
50     vector<int>ans;
51     for(int i=n;i>=1;i--)
52     {
53         int p=solve(a[i]+1);
54         add(p,-1);
55         ans.pb(p);
56     }
57     reverse(ans.begin(),ans.end());
58     for(auto &v:ans)
59         cout<<v<<'\n';
60 
61 
62 
63 
64 
65 }
View Code

 

题目链接:https://www.acwing.com/problem/content/description/262/ 

和上一题类似  如果从前往后模拟就要改变很多位置,倒序的话要改变的位置就只有一个

倒序来想的话就变成上一题,每次求第p+1小即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pi pair<int,int>
 5 #define pb push_back
 6 #define fi first
 7 #define sc second
 8 #define ull unsigned long long
 9 const int maxn=2e5+10;
10 const int mod=998244353;
11 
12 
13 int tr[maxn];
14 int v[maxn],p[maxn];
15 int n;
16 int ans[maxn];
17 
18 
19 void add(int x,int v)
20 {
21     while(x<=n) tr[x]+=v,x+=x&-x;
22 }
23 int query(int x)
24 {
25     int ans=0;
26     while(x) ans+=tr[x],x-=x&-x;
27     return ans;
28 }
29 int findk(int x)
30 {
31     int l=1,r=n;
32     while(l<r)
33     {
34         int mid=l+r>>1;
35         if(query(mid)>=x) r=mid;
36         else l=mid+1;
37     }
38     return l;
39 }
40 
41 int main()
42 {
43     ios::sync_with_stdio(0);
44     cin.tie(0);
45     while(cin>>n)
46     {
47         memset(tr,0,sizeof(tr));
48         for(int i=1;i<=n;i++)
49         {
50             cin>>p[i]>>v[i];
51             add(i,1);
52         }
53         for(int i=n;i>=1;i--)
54         {
55             int c=findk(p[i]+1);
56             ans[c]=v[i];
57             add(c,-1);
58         }
59         for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
60         cout<<'\n';
61     }
62 
63 
64 
65 
66 
67 }
View Code

 

题目链接:https://www.acwing.com/problem/content/description/1268/  校门外的树

经典区间交集问题,用两棵树状数组维护左端点和右端点的数量 每次查询的时候 l,r  sum1(r)-sum2(l-1)  

即包含r的左端点的数量减去不包含l的右端点的数量即可  因为r保证在右边,只需要要交集即可,即l能在

右端点的左边就能提供贡献 记住这个trick

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pi pair<int,int>
 5 #define pb push_back
 6 #define fi first
 7 #define sc second
 8 #define ull unsigned long long
 9 const int maxn=5e4+10;
10 const int mod=998244353;
11 
12 int tr[maxn][2];
13 int n,m;
14 void add(int x,int id)
15 {
16     while(x<=n) tr[x][id]++,x+=x&-x;
17 }
18 int query(int x,int id)
19 {
20     int res=0;
21     while(x) res+=tr[x][id],x-=x&-x;
22     return res;
23 }
24 
25 
26 
27 int main()
28 {
29     ios::sync_with_stdio(0);
30     cin.tie(0);
31     cin>>n>>m;
32     while(m--)
33     {
34         int k,l,r;
35         cin>>k>>l>>r;
36         if(k==1) add(l,0),add(r,1);
37         else cout<<query(r,0)-query(l-1,1)<<'\n';
38     }
39 
40 
41 
42 }
View Code

 

 

二维树状数组

题目链接:https://ac.nowcoder.com/acm/contest/965/F

记住模板即可 类似二维前缀和

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 #define pi pair<int,int>
 5 #define pb push_back
 6 #define fi first
 7 #define sc second
 8 #define ull unsigned long long
 9 const int maxn=5e3+10;
10 const int mod=998244353;
11 
12 
13 
14 ll tr[maxn][maxn];
15 
16 int n,m;
17 
18 
19 void add(int x,int y,int v)
20 {
21     while(x<=n)
22     {
23         int ty=y;
24         while(ty<=m) tr[x][ty]+=v,ty+=ty&-ty;
25         x+=x&-x;
26     }
27 }
28 ll query(int x,int y)
29 {
30     ll res=0;
31     while(x)
32     {
33         int ty=y;
34         while(ty) res+=tr[x][ty],ty-=ty&-ty;
35         x-=x&-x;
36     }
37     return res;
38 }
39 
40 int main()
41 {
42     ios::sync_with_stdio(0);
43     cin.tie(0);
44     cin>>n>>m;
45     int t;
46     while(cin>>t)
47     {
48         if(t==1)
49         {
50             int x,y,k;
51             cin>>x>>y>>k;
52             add(x,y,k);
53         }
54         else
55         {
56             int x1,y1,x2,y2;
57             cin>>x1>>y1>>x2>>y2;
58             cout<<query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1)<<'\n';
59         }
60     }
61 
62 
63 }
View Code

 

 

线段树   扫描线   套路题 记住即可 ###K

题目链接:https://www.acwing.com/problem/content/249/

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=2e4+10;
  4 const int mod=1e9+7;
  5 #define ll long long
  6 #define pi pair<int,int>
  7 #define fi first
  8 #define sc second
  9 #define pb push_back
 10 
 11 struct bc
 12 {
 13     int l,r,cnt;// 覆盖的次数
 14     double len;// 区间的长度
 15 };
 16 bc tr[maxn*4];
 17 
 18 struct ac
 19 {
 20     double x,y1,y2;
 21     int k;
 22     bool operator<(ac a)const
 23     {
 24         return x<a.x;
 25     }
 26 };
 27 ac a[maxn];
 28 vector<double>V;
 29 void dis()
 30 {
 31     sort(V.begin(),V.end());
 32     V.erase(unique(V.begin(),V.end()),V.end());
 33 }
 34 int id(double x)
 35 {
 36     return lower_bound(V.begin(),V.end(),x)-V.begin()+1;
 37 }
 38 
 39 void pushup(int x)
 40 {
 41     if(tr[x].cnt) tr[x].len=V[tr[x].r]-V[tr[x].l-1];//有被覆盖到,那么就拿区间的长度即可
 42     else if(tr[x].l!=tr[x].r) tr[x].len=tr[x<<1].len+tr[x<<1|1].len;//否则找儿子的区间长度
 43     else tr[x].len=0;// 减完要赋值为0
 44 }
 45 
 46 void build(int x,int l,int r)
 47 {
 48     tr[x]={l,r,0,0};
 49     if(l==r) return;
 50     int mid=(l+r)/2;
 51     build(x<<1,l,mid);
 52     build(x<<1|1,mid+1,r);
 53 }
 54 
 55 void update(int x,int l,int r,int v)
 56 {
 57     int L=tr[x].l,R=tr[x].r;
 58     if(l<=L&&R<=r)
 59     {
 60         tr[x].cnt+=v;
 61         pushup(x);
 62     }
 63     else
 64     {
 65         int mid=(L+R)/2;
 66         if(l<=mid) update(x<<1,l,r,v);
 67         if(r>mid) update(x<<1|1,l,r,v);
 68         pushup(x);
 69     }
 70 }
 71 
 72 
 73 
 74 int main()
 75 {
 76     ios::sync_with_stdio(0);
 77     cin.tie(0);
 78     int n,cnt=0;
 79     while(cin>>n)
 80     {
 81         if(!n) break;
 82         int tot=0;
 83         V.clear();
 84         for(int i=1;i<=n;i++)
 85         {
 86             double x1,y1,x2,y2;
 87             cin>>x1>>y1>>x2>>y2;
 88             a[++tot]={x1,y1,y2,1};
 89             a[++tot]={x2,y1,y2,-1};
 90             V.pb(y1),V.pb(y2);
 91         }
 92         dis();
 93         build(1,1,2*n);
 94 
 95 
 96         sort(a+1,a+2*n+1);
 97         double ans=0;
 98         for(int i=1;i<=2*n;i++)
 99         {
100             if(i>0) ans+=tr[1].len*(a[i].x-a[i-1].x);
101             update(1,id(a[i].y1),id(a[i].y2)-1,a[i].k);// i点代表 i~i+1 这一段区间
102         }
103         cout<<"Test case #"<<++cnt<<'\n';
104         cout<<"Total explored area: "<<fixed<<setprecision(2)<<ans<<'\n'<<'\n';
105     }
106 
107 
108 
109 
110 
111 }
View Code

 

题目链接:https://www.acwing.com/problem/content/1279/    ###K

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=1e5+10;
  4 const int mod=1e9+7;
  5 #define ll long long
  6 #define pi pair<int,int>
  7 #define fi first
  8 #define sc second
  9 #define pb push_back
 10 int n,m,p;
 11 
 12 struct ac
 13 {
 14     int l,r;
 15     ll sum,lazy1,lazy2;
 16     void update1(ll v)
 17     {
 18         sum=(sum+v*(r-l+1)%p)%p;;
 19         lazy1=(lazy1+v)%p;
 20     }
 21     void update2(ll v)
 22     {
 23         lazy1=lazy1*v%p;
 24         lazy2=lazy2*v%p;
 25         sum=sum*v%p;
 26     }
 27 
 28 };
 29 ac tr[maxn*4];
 30 ll a[maxn];
 31 
 32 void pushdown(int x)
 33 {
 34     ll v1=tr[x].lazy1,v2=tr[x].lazy2;
 35     if(v2!=1)
 36     {
 37         tr[x<<1].update2(v2);
 38         tr[x<<1|1].update2(v2);
 39         tr[x].lazy2=1;
 40     }
 41     if(v1)
 42     {
 43         tr[x<<1].update1(v1);
 44         tr[x<<1|1].update1(v1);
 45         tr[x].lazy1=0;
 46     }
 47 }
 48 
 49 void pushup(int x)
 50 {
 51     tr[x].sum=tr[x<<1].sum+tr[x<<1|1].sum;
 52     tr[x].sum%=p;
 53 }
 54 
 55 void build(int x,int l,int r)
 56 {
 57     tr[x]={l,r,0,0,1};
 58     if(l==r)
 59     {
 60         tr[x].sum=a[l];
 61     }
 62     else
 63     {
 64         int mid=(l+r)/2;
 65         build(x<<1,l,mid);
 66         build(x<<1|1,mid+1,r);
 67         pushup(x);
 68     }
 69 }
 70 
 71 void update(int x,int l,int r,ll v,int id)
 72 {
 73     int L=tr[x].l,R=tr[x].r;
 74     if(l<=L&&R<=r)
 75     {
 76         if(id) tr[x].update2(v);
 77         else tr[x].update1(v);
 78     }
 79     else
 80     {
 81         pushdown(x);
 82         int mid=(L+R)/2;
 83         if(l<=mid) update(x<<1,l,r,v,id);
 84         if(r>mid) update(x<<1|1,l,r,v,id);
 85         pushup(x);
 86     }
 87 }
 88 
 89 ll query(int x,int l,int r)
 90 {
 91     int L=tr[x].l,R=tr[x].r;
 92     if(l<=L&&R<=r)
 93     {
 94         return tr[x].sum;
 95     }
 96     else
 97     {
 98         pushdown(x);
 99         ll ans=0;
100         int mid=(L+R)/2;
101         if(l<=mid) ans=query(x<<1,l,r);
102         if(r>mid) ans=(ans+query(x<<1|1,l,r))%p;
103         return ans;
104     }
105 }
106 
107 
108 
109 
110 int main()
111 {
112     ios::sync_with_stdio(0);
113     cin.tie(0);
114     cin>>n>>p;
115     for(int i=1;i<=n;i++) cin>>a[i];
116     build(1,1,n);
117     int m;
118     cin>>m;
119     while(m--)
120     {
121         int c,l,r,v;
122         cin>>c>>l>>r;
123         if(c==1)
124         {
125             cin>>v;
126             update(1,l,r,v,1);
127         }
128         else if(c==2)
129         {
130             cin>>v;
131             update(1,l,r,v,0);
132         }
133         else
134         {
135             cout<<query(1,l,r)<<'\n';
136         }
137     }
138 
139 
140 }
View Code

考虑先做乘法还是先做加法  在pushdown的时候 如果是先加再乘的话 很难判断, 所以最好是全部都转换成先乘再加

那么 如何才能保证全部都是先乘再加还是正确的呢, 那么只需要 加的lazy标记 中 再乘法更新的时候 也乘上即可

举个例子 (sum+ lazy1)  * lazy2=sum*lazy2+lazy1*lazy2  所以只需要lazy1也乘上即可

不要用-1初始化lazy 因为更新lazy的时候 可能要直接加上或者直接乘上 要用相应的零元来初始化 乘法就是1 加法就是0

 

posted @ 2021-02-04 15:10  canwinfor  阅读(27)  评论(0)    收藏  举报