CF101D Castle

题目

CF101D Castle

分析

期望其实就是求这个平均值。

显然并不能直接贪心,因为涉及到某一个子树可以只走一次。(其实本质是某一条链会只走一次,不用返回根节点)

于是可以考虑 \(dp\) ,既然刚刚说到这个问题,我们就可以直接这样设,那么状态就是 \(dp[x]\) 表示 \(x\) 这个子树所有节点至少走完一次的权值和最小值。

考虑如何转移,首先我们发现直接不考虑子树的先后顺序,瞎转移的方程可以这样写:

\[\large dp[x]=\sum_{i}{(dp[v_i]+siz[v_i]\times w_i+(szt[v_i]+2w_i)\times \sum_{j>i}siz[j])} \]

然后我们肯定要安排顺序,这个时候我们可以考虑国王游戏那样的贪心,通过比较两个元素对答案的贡献来排序确定顺序。

这种方法适用于相邻两项之间对答案的贡献的差值只和这两项相关,和其他项无关。(或者说只能和这两项和其他定值相关,剩下的项都是不定值)

考虑列出两项 \(p,q\) 以及交换后的 dp 柿子,做差,发现贡献的变化量只和 \(p,q\) 有关,于是就可以直接排序确定最优方案了。

然后就直接根据朴素转移来即可。

时间复杂度 \(O(n\log n)\) 瓶颈在于排序。

代码

#include<bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDEGE
	#define getchar() rbuf[++pt]
	#define putchar(x) obuf[++pt1]=x
	char rbuf[(1<<21)+5],obuf[(1<<21)+5];
	int pt=-1,pt1=-1;
#endif
template<typename T>
inline void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)) f|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=f?-x:x;
	return ;
}
template<typename T>
void write(T x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int N=1e5+5,M=2005,INF=1e9+7;
int n,m;
int head[N],to[N<<1],nex[N<<1],val[N<<1],idx;
inline void add(int u,int v,int w){nex[++idx]=head[u];to[idx]=v;val[idx]=w;head[u]=idx;return ;}
#define PII pair<int,int>
#define mp make_pair
#define pb push_back
#define ll long long
ll dp[N];
int szt[N],siz[N];
inline bool cmp(const int &x,const int &y){return 1ll*szt[x]*siz[y]<1ll*szt[y]*siz[x];}
void dfs(int x,int f){
	vector<int>sta;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(y==f) continue;
		dfs(y,x);
		szt[y]+=(val[i]<<1);
		szt[x]+=szt[y],siz[x]+=siz[y];sta.pb(y);
		dp[x]+=dp[y]+1ll*siz[y]*val[i];
	}
	int sum=siz[x];siz[x]++;
	sort(sta.begin(),sta.end(),cmp);
	for(int y:sta) sum-=siz[y],dp[x]+=1ll*szt[y]*sum;
	return ;
}
signed main(){
#ifdef ONLINE_JUDEGE
	fread(rbuf,1,(1<<21),stdin);
#endif
	read(n);
	for(int i=1,u,v,w;i<n;i++) read(u),read(v),read(w),add(u,v,w),add(v,u,w);
	dfs(1,0);
	printf("%.8lf",(double)(dp[1]*1.0)/((n-1)*1.0));
#ifdef ONLINE_JUDEGE
	fwrite(obuf,1,pt1+1,stdout);
#endif
	return 0;
}
/*
9
1 4 1
1 5 1
2 5 2
3 5 2
5 7 2
4 6 2
8 4 2
9 4 1

*/
posted @ 2021-10-18 11:26  __Anchor  阅读(41)  评论(0)    收藏  举报