[線段樹&主席樹]の一些題解
[線段樹&主席樹] 一些題解
目錄
T1:The Child and Sequence
\(Description:\)
給你一個長度為\(n(n \leq 10^5)\)的序列,要求支援區間求和,區間取模,單點修改
\(Solution:\)
區間求和和單點修改都是基本的線段樹操作,關鍵在於區間取模。
這裡我們維護一個區間最大值,這樣如果模數大於當前區間的最大值,就不需要再往下走了
由於\(x \% y \leq\frac{x}{2}\),所以時間複雜度為\(O(nlog_2^2 n)\)
這樣時間效率能夠得到保證
\(Code:\)
#include<bits/stdc++.h> #define ll(x) (x<<1) #define rr(x) (x<<1|1) using namespace std; typedef long long lol; lol n,m,a[100005]; struct segment{ lol mmax,sum; }sgm[400005]; void push_up(lol root){ sgm[root].mmax=max(sgm[ll(root)].mmax,sgm[rr(root)].mmax); sgm[root].sum=sgm[ll(root)].sum+sgm[rr(root)].sum; return; } void build(lol root,lol left,lol right){ if(left==right){ sgm[root].mmax=a[left]; sgm[root].sum=a[left]; return; } if(left>right)return; lol mid=(left+right)>>1; build(ll(root),left,mid); build(rr(root),mid+1,right); push_up(root); } void insert_mod(lol root,lol left,lol right,lol l,lol r,lol mod){ if(sgm[root].mmax<mod)return; if(left>r||right<l)return; if(left==right){ sgm[root].mmax%=mod; sgm[root].sum%=mod; return; } lol mid=(left+right)>>1; insert_mod(ll(root),left,mid,l,r,mod); insert_mod(rr(root),mid+1,right,l,r,mod); push_up(root); } void insert_point(lol root,lol left,lol right,lol x,lol v){ if(left>x||right<x)return; if(left==right){ sgm[root].mmax=v; sgm[root].sum=v; return; } lol mid=(left+right)>>1; insert_point(ll(root),left,mid,x,v); insert_point(rr(root),mid+1,right,x,v); push_up(root); } lol query(lol root,lol left,lol right,lol l,lol r){ if(l<=left&&right<=r)return sgm[root].sum; if(right<l||left>r)return 0; lol mid=(left+right)>>1; return query(ll(root),left,mid,l,r)+query(rr(root),mid+1,right,l,r); } int main(){ scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++)scanf("%lld",&a[i]); build(1,1,n); for(int i=1;i<=m;i++){ lol opt,u,v,w; scanf("%lld",&opt); if(opt==1){ scanf("%lld%lld",&u,&v); printf("%lld\n",query(1,1,n,u,v)); } else if(opt==2){ scanf("%lld%lld%lld",&u,&v,&w); insert_mod(1,1,n,u,v,w); } else if(opt==3){ scanf("%lld%lld",&u,&w); insert_point(1,1,n,u,w); } } return 0; }
T2:Multiply game
\(Description:\)
給你一個長度為\(n(n \leq 10^5)\)的序列,維護區間乘積,支援單點修改
\(Solution:\)
線段樹板子。。。
\(Code:\)
#include<bits/stdc++.h> #define ll(x) (x<<1) #define rr(x) (x<<1|1) #define mod (1000000007) using namespace std; typedef long long lol; int T,n,m; lol a[50005]; struct segment{ lol mul; }sgm[200005]; void push_up(int root){ sgm[root].mul=sgm[ll(root)].mul*sgm[rr(root)].mul%mod; return; } void build(int root,int left,int right){ if(left==right){ sgm[root].mul=a[left]%mod; return; } if(left>right)return; int mid=(left+right)>>1; build(ll(root),left,mid); build(rr(root),mid+1,right); push_up(root); } void insert(int root,int left,int right,int l,int r,int x){ if(l<=left&&right<=r){ sgm[root].mul=x%mod; return; } if(l>right||r<left)return; int mid=(left+right)>>1; insert(ll(root),left,mid,l,r,x); insert(rr(root),mid+1,right,l,r,x); push_up(root); } lol query(int root,int left,int right,int l,int r){ if(l<=left&&right<=r)return sgm[root].mul%mod; if(l>right||r<left)return 1; int mid=(left+right)>>1; return query(ll(root),left,mid,l,r)*query(rr(root),mid+1,right,l,r)%mod; } int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n*4;i++) sgm[i].mul=1; for(int i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); scanf("%d",&m); while(m--){ int u,v,w; scanf("%d%d%d",&w,&u,&v); if(w==0){ printf("%lld\n",query(1,1,n,u,v)); } else if(w==1){ insert(1,1,n,u,u,v); } } } return 0; }
T3:Transformation
\(Description:\)
一個長度為\(n(n \leq 10^5)\)的序列,初始值全為零
維護區間的和,平方和,立方和
支援區間加法,區間乘法
\(Solution:\)
區間和很好維護,關鍵在於區間的平方和和立方和
討論區間\([l,r]\),令它們的和,平方和,立方和分別為\(S_1\),\(S_2\),\(S_3\)
顯然,對於區間乘法\(S_i\Rightarrow S_i\times c^i\)(\(c\)為乘上的數,\(i\in (1,2,3)\))
下面討論區間加法,令加數為c:
\(S_1\Rightarrow S_1+(r-l+1)\times c\)
\(S_2\Rightarrow S_2+2cS_1+(r-l+1)\times c^2\)
\(S_3\Rightarrow S_3+3cS_2+3c^2S_1+(r-l+1)\times c^3\)
由上述公式可以直接對三種和進行維護
\(Code:\)
#include<bits/stdc++.h>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
#define mod (10007)
using namespace std;
typedef long long lol;
int n,m;
lol Pow(lol x,int y){
lol ans=1;
x%=mod;
while(y){
if(y&1)ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
struct segment{
lol sum[4],lazy_add,lazy_mul,lazy_same;
void clear(){
for(int i=1;i<=3;i++)
sum[i]=0;
lazy_add=0;
lazy_same=-1;
lazy_mul=1;
}
void add(int l,int r,lol w){
int len=(r-l+1)%mod;
(sum[3]+=3*w%mod*sum[2]%mod+3*Pow(w,2)%mod*sum[1]%mod+Pow(w,3)*len%mod)%=mod;
(sum[2]+=2*w%mod*sum[1]%mod+Pow(w,2)*len%mod)%mod;
(sum[1]+=w*len%mod)%=mod;
}
}sgm[400005];
void push_up(int root){
for(int i=1;i<=3;i++)
sgm[root].sum[i]=(sgm[ll(root)].sum[i]+sgm[rr(root)].sum[i])%mod;
}
void push_down(int root,int left,int right){
if(left>right)return;
int mid=(left+right)>>1;
if(sgm[root].lazy_same!=-1){
lol w=sgm[root].lazy_same%mod;
for(int i=1;i<=3;i++){
sgm[ll(root)].sum[i]=(mid-left+1)%mod*Pow(w,i)%mod;
sgm[rr(root)].sum[i]=(right-mid)%mod*Pow(w,i)%mod;
}
sgm[ll(root)].lazy_add=sgm[rr(root)].lazy_add=0;
sgm[ll(root)].lazy_mul=sgm[rr(root)].lazy_mul=1;
sgm[ll(root)].lazy_same=sgm[rr(root)].lazy_same=w;
sgm[root].lazy_same=-1;
}
if(sgm[root].lazy_mul!=1){
lol w=sgm[root].lazy_mul%mod;
for(int i=1;i<=3;i++){
(sgm[ll(root)].sum[i]*=Pow(w,i))%=mod;
(sgm[rr(root)].sum[i]*=Pow(w,i))%=mod;
}
if(sgm[ll(root)].lazy_same!=-1)(sgm[ll(root)].lazy_same*=w)%=mod;
else{
(sgm[ll(root)].lazy_add*=w)%=mod;
(sgm[ll(root)].lazy_mul*=w)%=mod;
}
if(sgm[rr(root)].lazy_same!=-1)(sgm[rr(root)].lazy_same*=w)%=mod;
else{
(sgm[rr(root)].lazy_add*=w)%=mod;
(sgm[rr(root)].lazy_mul*=w)%=mod;
}
sgm[root].lazy_mul=1;
}
if(sgm[root].lazy_add){
lol w=sgm[root].lazy_add%mod;
sgm[ll(root)].add(left,mid,w);
sgm[rr(root)].add(mid+1,right,w);
if(sgm[ll(root)].lazy_same!=-1)(sgm[ll(root)].lazy_same+=w)%=mod;
else{(sgm[ll(root)].lazy_add+=w)%=mod;}
if(sgm[rr(root)].lazy_same!=-1)(sgm[rr(root)].lazy_same+=w)%=mod;
else{(sgm[rr(root)].lazy_add+=w)%=mod;}
sgm[root].lazy_add=0;
}
}
void insert(int root,int left,int right,int l,int r,int kind,lol w){
if(l<=left&&right<=r){
if(kind==1){
if(sgm[root].lazy_same!=-1)(sgm[root].lazy_same+=w)%=mod;
else{(sgm[root].lazy_add+=w)%=mod;}
sgm[root].add(left,right,w);
}
else if(kind==2){
if(sgm[root].lazy_same!=-1)(sgm[root].lazy_same*=w)%=mod;
else{
(sgm[root].lazy_add*=w)%=mod;
(sgm[root].lazy_mul*=w)%=mod;
}
for(int i=1;i<=3;i++)
(sgm[root].sum[i]*=Pow(w,i))%=mod;
}
else if(kind==3){
sgm[root].lazy_add=0;
sgm[root].lazy_mul=1;
sgm[root].lazy_same=w;
for(int i=1;i<=3;i++)
sgm[root].sum[i]=(right-left+1)%mod*Pow(w,i)%mod;
}
return;
}
if(left>r||l>right)return;
push_down(root,left,right);
int mid=(left+right)>>1;
insert(ll(root),left,mid,l,r,kind,w);
insert(rr(root),mid+1,right,l,r,kind,w);
push_up(root);
}
lol query(int root,int left,int right,int l,int r,int t){
if(l<=left&&right<=r)return sgm[root].sum[t]%mod;
if(left>r||l>right)return 0;
push_down(root,left,right);
int mid=(left+right)>>1;
return (query(ll(root),left,mid,l,r,t)+query(rr(root),mid+1,right,l,r,t))%mod;
}
int main(){
freopen("Cin.txt","r",stdin);
while(1){
scanf("%d%d",&n,&m);
if(n==0&&m==0)break;
for(int i=1;i<=n*4;i++)
sgm[i].clear();
for(int i=1;i<=m;i++){
int k,u,v;
lol w;
scanf("%d%d%d%lld",&k,&u,&v,&w);
w%=mod;
if(k==4)printf("%lld\n",query(1,1,n,u,v,w));
else{insert(1,1,n,u,v,k,w);}
}
}
return 0;
}
T4:Legacy
\(Description:\)
一張有\(n(n \leq 10^5)\)個點的圖,由\(q(q \leq 10^5)\)個操作,型別如下:
1.從\(u\)到\(v\)連一條長度為w的邊
2.從\(u\)到\([l,r]\)區間內的每一點都連一條長度為\(w\)的邊
3.從\([l,r]\)區間內的每一點到\(v\)都連一條長度為\(w\)的邊
求從\(s\)到每個節點的最短距離,不能到達則為\(-1\)
\(Solution:\)
顯然,如果圖建完了,就只要跑一遍最短路就完事,但是事實不是這樣啊
考慮到這有可能是一張完全圖,所以如果真的每一條邊老老實實建,時間空間就...起飛~
由於每次連邊的是相鄰的一個區間,我們考慮線段樹優化建圖:
建立線段樹\(A\),每一個子節點向父節點連邊,邊權為\(0\)
建立線段樹\(B\),每一個父節點向子節點連邊,邊權為\(0\)
\(B\)的每一個葉子節點向\(A\)的對應葉子節點連邊,邊權為\(0\)
對於操作\(1\),我們將\(A\)的第\(u\)個葉子節點向\(B\)的第\(v\)個葉子節點連一條權值為\(w\)的邊
對於操作\(2\),我們將\(A\)的第\(u\)個葉子節點向\(B\)的\([l,r]\)區間節點連一條權值為\(w\)的邊
對於造作\(3\),我們將\(A\)的\([l,r]\)區間節點向\(B\)的第\(v\)個葉子節點連一條權值為\(w\)的邊
這樣每次的建邊數量為\(log_2^2 n\)條,時間空間效率就有了保證
最後的結果就是\(A\)的第\(s\)個葉子節點到其他葉子節點的最短路
\(Code:\)
#include<bits/stdc++.h>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
using namespace std;
typedef long long lol;
const int N=100005;
const lol inf=1e18+5;
int tot,n,m,l,a[N],b[N];
struct node{
int to;
lol dis;
node(int _to,lol _dis){to=_to;dis=_dis;}
};
vector<node>edge[N<<3];
struct segment{
int num;
}sgm_a[N<<2],sgm_b[N<<2];
void build_a(int root,int left,int right){
sgm_a[root].num=++tot;
if(left==right){
a[left]=sgm_a[root].num;
return;
}
if(left>right)return;
int mid=(left+right)>>1;
build_a(ll(root),left,mid);
build_a(rr(root),mid+1,right);
edge[sgm_a[ll(root)].num].push_back(node(sgm_a[root].num,0ll));
edge[sgm_a[rr(root)].num].push_back(node(sgm_a[root].num,0ll));
}
void insert_a(int root,int left,int right,int l,int r,int s,lol dis){
if(l<=left&&right<=r){
edge[sgm_a[root].num].push_back(node(b[s],dis));
return;
}
if(l>right||left>r)return;
int mid=(left+right)>>1;
insert_a(ll(root),left,mid,l,r,s,dis);
insert_a(rr(root),mid+1,right,l,r,s,dis);
}
void build_b(int root,int left,int right){
sgm_b[root].num=++tot;
if(left==right){
b[left]=sgm_b[root].num;
return;
}
if(left>right)return;
int mid=(left+right)>>1;
build_b(ll(root),left,mid);
build_b(rr(root),mid+1,right);
edge[sgm_b[root].num].push_back(node(sgm_b[ll(root)].num,0ll));
edge[sgm_b[root].num].push_back(node(sgm_b[rr(root)].num,0ll));
}
void insert_b(int root,int left,int right,int l,int r,int s,lol dis){
if(l<=left&&right<=r){
edge[a[s]].push_back(node(sgm_b[root].num,dis));
return;
}
if(l>right||left>r)return;
int mid=(left+right)>>1;
insert_b(ll(root),left,mid,l,r,s,dis);
insert_b(rr(root),mid+1,right,l,r,s,dis);
}
int vis[N<<3];
lol dis[N<<3];
void dijkstra(int r){
memset(vis,0,sizeof(vis));
for(int i=1;i<=(n<<3);i++)dis[i]=inf;
dis[r]=0;
pair<lol,int>tmp;
priority_queue<pair<lol,int>,vector<pair<lol,int>>,greater<pair<lol,int>>>q;
q.push(pair<lol,int>(0ll,r));
while(!q.empty()){
tmp=q.top();q.pop();
int u=tmp.second;
if(tmp.first>dis[u])continue;
vis[u]=1;
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i].to;
if(vis[v])continue;
if(dis[v]>dis[u]+edge[u][i].dis){
dis[v]=dis[u]+edge[u][i].dis;
q.push(pair<lol,int>(dis[v],v));
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&l);
build_a(1,1,n);
build_b(1,1,n);
for(int i=1;i<=n;i++)edge[b[i]].push_back(node(a[i],0ll));
while(m--){
int k,u,v,s;
lol w;
scanf("%d",&k);
if(k==1){
scanf("%d%d%lld",&u,&v,&w);
edge[a[u]].push_back(node(b[v],w));
}
else if(k==2){
scanf("%d%d%d%lld",&s,&u,&v,&w);
insert_b(1,1,n,u,v,s,w);
}
else if(k==3){
scanf("%d%d%d%lld",&s,&u,&v,&w);
insert_a(1,1,n,u,v,s,w);
}
}
dijkstra(a[l]);
for(int i=1;i<=n;i++){
if(dis[a[i]]!=inf){
printf("%lld ",dis[a[i]]);
}
else printf("-1 ");
}
return 0;
}
(未完待續。。。)