• [题解] LuoguP5666 树的重心


    这个题......确实是CSPNOIP题qwq

    感觉猜到一个性质就差不多了,首先,对于一棵树,随便拎一个节点(rt)当根节点,那么他的重心一定在(rt)的重儿子里,进一步的,可以发现重心一定在(rt)向下的重链上。

    根据这条性质,以及重链上从上到下节点的(size)一定是递减的,所以考虑在重链上倍增。

    具体的令(f[x][i])表示(rt)为根的时候,节点(x)沿着重链向下走(2^i)步到达的节点,这样,求重心从大到小枚举(i),向下跳就好了,另外一些细节可以自己画一个简单的图感性理解一下(qwq)

    基于上面的那个求重心的方法,类似换根(dp),二次扫描维护(f)和重儿子求答案就好了。

    实现详见代码(配合c++11食用qaq):

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define mp make_pair
    #define all(x) (x).begin(),(x).end()
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    typedef long long ll;
    typedef double db;
    typedef pair<int,int> PII;
    typedef vector<int> VI;
    
    const int N=3e5+10,LOGN=20;
    VI g[N];
    int n,siz[N],f[N][23],son[N],fa[N];
    ll ans;
    
    void getf(int x) {
    	rep(i,1,LOGN) f[x][i]=f[f[x][i-1]][i-1];
    }
    void calc(int x) {
    	int u=x; 
    	per(i,0,LOGN) {
    		if(f[u][i]&&siz[f[u][i]]*2>=siz[x]) // 这里可以画张图感性理解一下qwq
    			u=f[u][i];
    	}
    	if(siz[u]*2==siz[x]) ans+=fa[u];
    	ans+=u;
    }
    
    void dfs(int x,int ff) {
    	fa[x]=ff;
    	siz[x]=1,siz[son[x]=0]=0;
    	for(auto v:g[x]) if(v!=ff) {
    		dfs(v,x),siz[x]+=siz[v];
    		if(siz[v]>siz[son[x]]) son[x]=v;
    	}
    	f[x][0]=son[x],getf(x);
    }
    
    void getans(int x,int ff) {
    	int x1=0,x2=0; siz[0]=0;
    	for(auto v:g[x]) { // 枚举与x相邻的点,算出最大的儿子和次大的儿子
    		if(siz[v]>=siz[x1]) x2=x1,x1=v;
    		else if(siz[v]>=siz[x2]) x2=v;
    	}
    	for(auto v:g[x]) if(v!=ff) {
    		calc(v);
    		f[x][0]=v==x1?x2:x1,getf(x); // 
    		siz[x]-=siz[v],siz[v]+=siz[x];
    		calc(x);
    		fa[x]=v,getans(v,x);
    		siz[v]-=siz[x],siz[x]+=siz[v]; // 在算完边(x,v)对答案的贡献的时候要注意撤回影响
    	}
    	f[x][0]=son[x]; // 回溯的时候撤销操作
    	getf(x),fa[x]=ff;
    }
    
    void sol() {
    	memset(siz,0,sizeof(siz));
    	memset(son,0,sizeof(son));
    	memset(f,0,sizeof(f));
    	memset(fa,0,sizeof(fa)); // 多测不清空抱凛两行泪TAT
    	ans=0; scanf("%d",&n);
    	rep(i,0,n+1) g[i].clear();
    	rep(i,0,n-1) {
    		int x,y; scanf("%d%d",&x,&y);
    		g[x].pb(y),g[y].pb(x);
    	}
    	dfs(1,0),getans(1,0);
    	printf("%lld
    ",ans);
    }
    
    int main() {
    	int _; scanf("%d",&_); while(_--) sol();
    	return 0;
    }
    

    另外,听说NOIP复活了?

  • 相关阅读:
    助教小结4
    第二次作业
    助教小结5
    助教小结3
    work3
    助教小结1
    课后第一次作业
    助教小结2
    第一次团队作业
    悟透 JavaScript
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12340948.html
Copyright © 2020-2023  润新知