题意
给一颗树,树上每个点有权值val,定义Sum=Σ(D(u,v))(1<=u,v<=n)Sum=Σ(D(u,v))(1<=u,v<=n),D(u,v)D(u,v)为点u到v的路径权值和。
题解
定义dp[u]dp[u]表示经过点u的路径数量,因此Sum=Σ(dp[u]∗val[u])Sum=Σ(dp[u]∗val[u])
分类讨论
1. Sum=0Sum=0,所以ans=0ans=0
2. 存在Sum%dp[u]=0Sum%dp[u]=0,所以ans=1ans=1
3. 我们可以知道假如存在gcd(dp[u],dp[v])=1gcd(dp[u],dp[v])=1,那么肯定能使得通过调整val[u]val[u],val[v]val[v],使得Sum=0Sum=0,但是在这题中这样的两个点必定存在。证明:首先找到一个深度最大的叶子节点,那么其dp[u]=2∗n−1dp[u]=2∗n−1,其父节点dp[parent]=6∗n−1dp[parent]=6∗n−1,那么gcd(2∗n−1,6∗n−11)=gcd(2∗n−1,−8)gcd(2∗n−1,6∗n−11)=gcd(2∗n−1,−8)由于2*n-1是奇数,因此gcd(2∗n−1,−8)=1gcd(2∗n−1,−8)=1
AC代码
#include<stdio.h>
#include<vector>
#define N 100005
using namespace std;
typedef long long ll;
vector<ll>vt[N];
ll a[N],dp[N],size[N],dep[N],Fa[N],n,ans,nowdep;
void dfs(ll u,ll fa)
{
dep[u]=dep[fa]+1;
Fa[u]=fa;
size[u]=1;
if(nowdep<dep[u])nowdep=dep[u],ans=u;
for(ll i=0;i<vt[u].size();i++)
{
ll to=vt[u][i];
if(to==fa)continue;
dfs(to,u);
size[u]+=size[to];
dp[u]+=(n-size[to]-1)*size[to];
}
dp[u]+=(n-size[u])*(size[u]-1);
dp[u]+=(n-1)*2+1;
}
int main()
{
ll T;
scanf("%lld",&T);
while(T--)
{
nowdep=0;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
vt[i].clear();
dp[i]=dep[i]=0;
}
for(ll i=0;i<n-1;i++)
{
ll u,v;
scanf("%lld%lld",&u,&v);
vt[u].push_back(v);
vt[v].push_back(u);
}
dfs(1,1);
ll sum=0;
for(ll i=1;i<=n;i++)
sum+=dp[i]*a[i];
if(sum==0)
{
printf("0\n");
continue;
}
ll flag=0;
for(ll i=1;i<=n;i++)
if(sum%dp[i]==0)
{
printf("1\n%lld\n",i);
flag=1;
break;
}
if(flag)continue;
printf("2\n%lld %lld\n",Fa[ans],ans);
}
}


被折叠的 条评论
为什么被折叠?



