植物學家

【簡要題意】一棵帶點權的有根樹,其中樹根可以轉換。每次詢問當前根下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,知道更多奇葩解法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章