loj 146

题意

给一棵节点数为n的有根树和根的编号,树上的节点有点权。有m次操作,操作分为3种:询问一个点的点权,询问以一个点为根的子树的点权,将一条链上的点的点权加上一个权值。

数据范围

1 n , m 1 e 6 , 1 e 6 1 e 6 1\le n,m \le 1e6,-1e6\le 点权 \le 1e6

解法: dfs序+树上差分+树状数组

(题外话:树剖做这个应该非常板,可惜复杂度高了)
首先对于修改操作,可以用树上差分,其中lca部分使用 O ( n l o g n ) O ( 1 ) O(nlogn)预处理,O(1)查询的方法:

前置知识: O ( 1 ) l c a O(1)查询的lca

使用欧拉序(不是括号序):特点是每个点会被记录度数次。所以数组记得开2倍(其实括号序也是2倍),预处理的时候也要多预处理一部分。
然后两个点的lca,就是它们首次在欧拉序中出现的两个位置形成的这段区间里深度最小的点。然后可以用st表

如果使用了树上差分,就要考虑在线的问题,所以树上差分的修改直接用树状数组做了。这样如何查询点权呢?考虑建出括号序,然后每个点在括号序上有两个位置,直接查这个区间的值就可以了,这个本质是树状数组区间修改,单点查询的做法,树状数组维护的是差分数组。
然后是查询子树权值:这个可以考虑另开一个树状数组记录子树内权值的变化(其实还是比较套路):对每个点记录经过该点的被修改的链在该点子树内的点的[点权×深度],包括这个点本身的和。这样的话,就可以用这个值减去该点权值×(该点深度-1)得到整棵子树的权值了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e6+5;
typedef long long ll;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,v[maxn],rt;
struct edge{
	int v,p;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	h[b]=cnt;
}
int tim,lpos[maxn],rpos[maxn],st[maxn][23],f[maxn],dep[maxn],fp[maxn],tot;
ll c[maxn][2];
void dfs(int u,int fa){
	f[u]=fa;lpos[u]=++tim;fp[u]=++tot;
	dep[u]=dep[fa]+1;st[tot][0]=u;
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);tot++;
		st[tot][0]=u;
	}
	rpos[u]=tim;
}
inline int lowbit(int x){return x&(-x);}
inline void add(int t,int pos,ll x){if(pos==0)return ;for(;pos<=n;pos+=lowbit(pos))c[pos][t]+=x;}
inline ll query(int t,int pos){ll ans=0;for(;pos;pos-=lowbit(pos))ans+=c[pos][t];return ans;}
inline ll qs(int t,int l,int r){return query(t,r)-query(t,l-1);}
inline ll qn(int x){return qs(0,lpos[x],rpos[x]);}
inline ll qt(int x){return qs(1,lpos[x],rpos[x])-qs(0,lpos[x],rpos[x])*(dep[x]-1);}
int lg[maxn];
inline int lca(int a,int b){
	if(fp[b]<fp[a])swap(a,b);
	int k=lg[fp[b]-fp[a]+1];
	return dep[st[fp[a]][k]]<dep[st[fp[b]-(1<<k)+1][k]]?st[fp[a]][k]:st[fp[b]-(1<<k)+1][k];
}
void add_chain(int a,int b,int x){
	int l=lca(a,b);
	//printf("%d %d %d\n",a,b,l);
	add(0,lpos[a],x);add(0,lpos[b],x);
	add(0,lpos[l],-x);add(0,lpos[f[l]],-x);
	add(1,lpos[a],1ll*x*dep[a]);add(1,lpos[b],1ll*x*dep[b]);
	add(1,lpos[l],-1ll*x*dep[l]);add(1,lpos[f[l]],-1ll*x*dep[f[l]]);
}
inline void print(ll x){
	if(x==0){puts("0");return ;}
	if(x<0){putchar('-');x*=-1;}
	char s[55];
	memset(s,0,sizeof(s));
	int cnt=-1;
	while(x){cnt++;
		s[cnt]=x%10+'0';x/=10;
	}
	for(int i=cnt;i>=0;i--)putchar(s[i]);
	puts("");
}
signed main(){
	//freopen("146.in","r",stdin);
	//freopen("146.out","w",stdout);
	n=read(),m=read(),rt=read();
	for(int i=1;i<=n;i++)v[i]=read();
	for(int i=1;i<n;i++){
		int a=read(),b=read();
		add(a,b);
	}
	dfs(rt,0);
	for(int i=2;i<=tot;i++)lg[i]=lg[i>>1]+1;
	//for(int i=1;i<=tot;i++)
	//printf("%d ",st[i][0]);
	//puts("");
	for(int j=1;(1<<j)<=tot;j++){
		for(int i=1;i+(1<<j-1)<=tot;i++){
			st[i][j]=dep[st[i][j-1]]<dep[st[i+(1<<j-1)][j-1]]?st[i][j-1]:st[i+(1<<j-1)][j-1];
		}
	}
	int opt,a,b,x;
	for(int i=1;i<=n;i++){add_chain(i,i,v[i]);}
	while(m--){
		opt=read();
		if(opt==1){
			a=read(),b=read(),x=read();
			add_chain(a,b,x);
		}
		if(opt==2){
			x=read();
			ll tmp=qn(x);
			print(tmp);
		}
		if(opt==3){
			x=read();
			ll tmp=qt(x);
			print(tmp);
		}
	}
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e6+5;
typedef long long ll;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,v[maxn],rt;
struct edge{
	int v,p;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	h[b]=cnt;
}
int tim,lpos[maxn],rpos[maxn],st[maxn][23],f[maxn],dep[maxn],fp[maxn],tot;
ll c[maxn][2];
void dfs(int u,int fa){
	f[u]=fa;lpos[u]=++tim;fp[u]=++tot;
	dep[u]=dep[fa]+1;st[tot][0]=u;
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);tot++;
		st[tot][0]=u;
	}
	rpos[u]=tim;
}
inline int lowbit(int x){return x&(-x);}
inline void add(int t,int pos,ll x){if(pos==0)return ;for(;pos<=n;pos+=lowbit(pos))c[pos][t]+=x;}
inline ll query(int t,int pos){ll ans=0;for(;pos;pos-=lowbit(pos))ans+=c[pos][t];return ans;}
inline ll qs(int t,int l,int r){return query(t,r)-query(t,l-1);}
inline ll qn(int x){return qs(0,lpos[x],rpos[x]);}
inline ll qt(int x){return qs(1,lpos[x],rpos[x])-qs(0,lpos[x],rpos[x])*(dep[x]-1);}
int lg[maxn];
inline int lca(int a,int b){
	if(fp[b]<fp[a])swap(a,b);
	int k=lg[fp[b]-fp[a]+1];
	return dep[st[fp[a]][k]]<dep[st[fp[b]-(1<<k)+1][k]]?st[fp[a]][k]:st[fp[b]-(1<<k)+1][k];
}
void add_chain(int a,int b,int x){
	int l=lca(a,b);
	//printf("%d %d %d\n",a,b,l);
	add(0,lpos[a],x);add(0,lpos[b],x);
	add(0,lpos[l],-x);add(0,lpos[f[l]],-x);
	add(1,lpos[a],1ll*x*dep[a]);add(1,lpos[b],1ll*x*dep[b]);
	add(1,lpos[l],-1ll*x*dep[l]);add(1,lpos[f[l]],-1ll*x*dep[f[l]]);
}
inline void print(ll x){
	if(x==0){puts("0");return ;}
	if(x<0){putchar('-');x*=-1;}
	char s[55];
	memset(s,0,sizeof(s));
	int cnt=-1;
	while(x){cnt++;
		s[cnt]=x%10+'0';x/=10;
	}
	for(int i=cnt;i>=0;i--)putchar(s[i]);
	puts("");
}
signed main(){
	//freopen("146.in","r",stdin);
	//freopen("146.out","w",stdout);
	n=read(),m=read(),rt=read();
	for(int i=1;i<=n;i++)v[i]=read();
	for(int i=1;i<n;i++){
		int a=read(),b=read();
		add(a,b);
	}
	dfs(rt,0);
	for(int i=2;i<=tot;i++)lg[i]=lg[i>>1]+1;
	//for(int i=1;i<=tot;i++)
	//printf("%d ",st[i][0]);
	//puts("");
	for(int j=1;(1<<j)<=tot;j++){
		for(int i=1;i+(1<<j-1)<=tot;i++){
			st[i][j]=dep[st[i][j-1]]<dep[st[i+(1<<j-1)][j-1]]?st[i][j-1]:st[i+(1<<j-1)][j-1];
		}
	}
	int opt,a,b,x;
	for(int i=1;i<=n;i++){add_chain(i,i,v[i]);}
	while(m--){
		opt=read();
		if(opt==1){
			a=read(),b=read(),x=read();
			add_chain(a,b,x);
		}
		if(opt==2){
			x=read();
			ll tmp=qn(x);
			print(tmp);
		}
		if(opt==3){
			x=read();
			ll tmp=qt(x);
			print(tmp);
		}
	}
	return 0;
}

发布了62 篇原创文章 · 获赞 1 · 访问量 1016

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103682909
今日推荐