CF1527D MEX Tree 题解
SunsetLake · · 题解
思路
如果一条路径的
知道这个以后我们就得到了一个大致思路:从小到大枚举
那么,如何判断
做完这步,还有一个问题就是:如何求满足条件的路径数量?可以先从
- 所有的
f_v 。 - 以
u 的子树中的任意一点为起点,u 为终点的路径。即size_u-1 。 -
这样我们就求出了
于是初始化左端点为
其余细节见注释,赛时写的代码,可能有点丑,见谅。
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,n,pp;
int head[200005],cnt,k,vis[200005];
int ft[200005];
ll sz[200005];
ll f[200005],g[200005];
struct edge{
int v,nxt;
}e[400005];
void add(int u,int v){
e[++k].v=v;
e[k].nxt=head[u];
head[u]=k;
}
void dfs(int u,int fa){
ll res=0,sum=0;
sz[u]=1;
ft[u]=fa;
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
g[u]+=f[v];
sz[u]+=sz[v];
res+=sz[v];
sum+=sz[v]*sz[v];
}
res*=res;
f[u]+=g[u]+sz[u]-1+(res-sum)/2;//计算f[u]
}
void solve(){
cin>>n;
k=0;
for(int i=0;i<=n;++i){
sz[i]=0;
head[i]=-1;
f[i]=g[i]=vis[i]=0;
}
for(int i=1;i<n;++i){
int a,b;
cin>>a>>b;
add(a,b);add(b,a);
}
dfs(0,-1);
cout<<g[0]<<" ";
int now=1;
vis[0]=1;
while(ft[now]!=0){//跳1的父亲
vis[now]=1;
now=ft[now];
}
vis[now]=1;
pp=now;
cout<<f[0]-g[0]-sz[1]*(n-sz[now])<<" ";
int l=1,r=0;//维护链的端点
bool flag=0;
int epos=0;
for(int i=2;i<=n;++i){
now=i;
int po=0;
while((!vis[ft[now]])){//向上跳
po++;
vis[now]=1;
now=ft[now];
}
int ffa=ft[now];
if((ffa!=l)&&(ffa!=r)&&(po||(po==0&&(!vis[i])))){//不在链上
flag=1;
if(r!=0)cout<<sz[l]*sz[r]<<' ';
else cout<<sz[l]*(sz[r]-sz[pp])<<" ";
epos=i+1;
break;
}
if(now==i&&po==0&&vis[i]){//已经在链上了,那就不可能把0~k-1走完,答案为0
cout<<0<<" ";
continue;
}
vis[now]=1;
if(ffa==l){//更新左右端点
if(r!=0)cout<<(sz[l]-sz[i])*sz[r]<<" ";
else cout<<(sz[l]-sz[i])*(sz[r]-sz[pp])<<' ';
l=i;
}
else{
if(r!=0)cout<<(sz[r]-sz[i])*sz[l]<<' ';
else cout<<(sz[r]-sz[pp]-sz[i])*sz[l]<<" ";
r=i;
}
}
if(flag){//不在链上全部输出0
for(int i=epos;i<=n;++i)cout<<"0 ";
cout<<endl;
return;
}
cout<<endl;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)solve();
return 0;
}