题解 - C F 1401 D \mathrm{CF1401D} CF1401D
题目意思
S o l \mathrm{Sol} Sol
简单树上贪心
我们肯定考虑一条边的权值 v i v_i vi 左边有 l s ls ls 个点右边就 ( n − l s ) (n-ls) (n−ls) 个点,那么这条边的贡献为 l s ( n − l s ) × v i ls(n-ls)\times v_i ls(n−ls)×vi
那么我们肯定尽量让大的权值的贡献最大,从大到小排个序累加一下即可,剩下的就是简单模拟一下。
时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
C o d e \mathrm{Code} Code
const int N=2e5+5;
int n,m,siz[N],head[N],cnt,P[N],S[N],bo[N],cwy,ans;
struct Node
{
int nex,to;
};
Node e[N<<1];
inline void jia(int u,int v)
{
e[++cnt].nex=head[u];
head[u]=cnt;
e[cnt].to=v;
}
inline void dfs(int u,int fa)
{
siz[u]=1;
GO(i,u)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
S[++cwy]=siz[v]*(n-siz[v]);//记录每条边的贡献
}
}
inline bool Cmp(int x,int y) { return x>y; }
inline void Add(int &x,int y) { x+=y; if(x>=mod) x-=mod; }
signed main()
{
int Q;
io.read(Q);
for (;Q--;)
{
io.read(n);
For(i,1,n) head[i]=0,siz[i]=0;
cwy=cnt=ans=0;
For(i,1,n-1)
{
int x,y;
io.read(x),io.read(y);
jia(x,y);
jia(y,x);
}
io.read(m);
For(i,1,m) io.read(P[i]);
dfs(1,0);
sort(P+1,P+m+1,Cmp);
sort(S+1,S+cwy+1,Cmp);//分别从大到小排序
if(m<n)
{
For(i,m+1,n) P[i]=1;//不足n-1条边就用1补齐
For(i,1,n-1) Add(ans,P[i]*S[i]%mod);
io.write((ans+mod)%mod);
puts("");
}
else
{
int st=m-n+2;
int tot=1ll;
For(i,1,st) tot=tot*P[i]%mod;
ans=tot*S[1]%mod;
//否则把前面几个大的合并记录贡献
For(i,2,n-1) Add(ans,P[st+i-1]*S[i]%mod);
io.write((ans+mod)%mod);
puts("");
}
}
return 0;
}