植物学家

版权声明:写得不好,随便转载,但请注明出处,感激不尽 https://blog.csdn.net/xyc1719/article/details/87453451

【简要题意】一棵带点权的有根树,其中树根可以转换。每次询问当前根下x节点的子树权值和。
节点数、操作数<=1e5

【分析】
对于任意一个点做根,相当于是在整棵树总的权值中减去根所在的那颗"子树"。可以先固定根,求dfs序确定root所在的位置,再通过lca确定哪棵子树。

【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+1000;
int idx[maxn],beg[maxn],end[maxn],dfs_clock;
struct Edge{
	int v,nxt;
}edge[maxn<<1];
int head[maxn],tot;
int n,m,root,val[maxn],siz[maxn],sum;
int p[maxn][20],d[maxn];
inline void read(int &x){
	x=0;char tmp=getchar();int fl=1;
	while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
	while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
	x=x*fl;
}
void dfs(int u,int dep){
	idx[u]=++dfs_clock;beg[u]=idx[u]+1;
	siz[u]=val[u],d[u]=dep;
	for(int i=head[u];i!=-1;i=edge[i].nxt){
		int v=edge[i].v;if(d[v]) continue;
		p[v][0]=u;dfs(v,dep+1);siz[u]+=siz[v];
	} end[u]=dfs_clock;
}
inline int lca(int x,int y){for(int i=18;i>=0;i--)if(d[y]-d[x]>(1<<i)) y=p[y][i];return y;}
int main(){
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	for(int i=1;i<n;i++){
		int x,y;read(x),read(y);
		edge[tot]=(Edge){y,head[x]},head[x]=tot++;
		edge[tot]=(Edge){x,head[y]},head[y]=tot++;
	}
	for(int i=1;i<=n;i++) read(val[i]);
	cin>>root;dfs(root,1);sum=siz[root];p[root][0]=root;
	for(int j=1;j<=18;j++)for(int i=1;i<=n;i++)p[i][j]=p[p[i][j-1]][j-1];
	for(int i=1;i<=m;i++){
		int opt,x;read(opt),read(x);
		if(opt==1) root=x;
		else if(root==x) printf("%d\n",sum);
		else if(idx[root]>=beg[x]&&idx[root]<=end[x]) printf("%d\n",sum-siz[lca(x,root)]);
		else printf("%d\n",siz[x]);
	}
	return 0;
}

这当中的方法其实非常多,像求dfs序之后二分查找求根,树链剖分树上分块(请选择正确的分块方法),由于随机数据甚至放过了暴力换根。欢迎光顾HG的其他blogs,知道更多奇葩解法。

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/87453451