树状数组/线段树总结
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 }
题目链接: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 }
题目链接: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 }
二维树状数组
题目链接: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 }
线段树 扫描线 套路题 记住即可 ###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 }
题目链接: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 }
考虑先做乘法还是先做加法 在pushdown的时候 如果是先加再乘的话 很难判断, 所以最好是全部都转换成先乘再加
那么 如何才能保证全部都是先乘再加还是正确的呢, 那么只需要 加的lazy标记 中 再乘法更新的时候 也乘上即可
举个例子 (sum+ lazy1) * lazy2=sum*lazy2+lazy1*lazy2 所以只需要lazy1也乘上即可
不要用-1初始化lazy 因为更新lazy的时候 可能要直接加上或者直接乘上 要用相应的零元来初始化 乘法就是1 加法就是0

浙公网安备 33010602011771号