[线段树&主席树]の一些题解

[线段树&主席树] 一些题解

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;
}

(未完待续。。。)

猜你喜欢

转载自www.cnblogs.com/huangdalaofighting/p/13377891.html
今日推荐