CF1554E You 题解 性质题
题目传送门
\text{Description}
-
对于一棵有
n 个节点的树,做n 次操作。 -
每次操作删去一个点
i ,a_i 为此时与a_i 相邻的未被删去的点数量。 -
对于所有不同的
\{a_n\} ,令k=\gcd(a_1,...,a_n) 。 -
求当
k=1,...,n 时的\{a_n\} 方案数。
\text{Solution}
考虑到相当于给边定向,对于每条边指向的点
则易知总共有
考虑到
我们考虑
这里有一个特别重要的性质。
我们令
我们简单证一下。
首先
然后与叶子节点相连的点就必须先删掉,也就是与叶子节点相连的边就定向了。
然后我们依次向上考虑
因此
同时在这个证明过程中,由于
inline int dfs(int x,int fa){
if(!flag) return 0;
int tot=es[x].size();
for(re i=0;i<es[x].size();++i)
if(es[x][i]!=fa) tot-=dfs(es[x][i],x);
if(tot%k&&(tot-1)%k) flag=0;
if(tot%k==0){ans=gcd(ans,tot);return 1;}
else{ans=gcd(ans,tot-1);return 0;}
}
由于
所以我们把每个
时间复杂度
然而这个显然还可以优化。(
由于
那么我们可以直接找
时间复杂度
\text{Code}
const int N=1e5+5,mod=998244353;
int T,n,a[N],ans,k;
bool flag;
vector<int> es[N];
inline int pw(int x,int y){
int sum=1;
while(y){
if(y&1) sum=1ll*sum*x%mod;
x=1ll*x*x%mod;y>>=1;
}
return sum;
}
inline int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
inline int dfs(int x,int fa){
if(!flag) return 0;
int tot=es[x].size();
for(re i=0;i<es[x].size();++i)
if(es[x][i]!=fa) tot-=dfs(es[x][i],x);
if(tot%k&&(tot-1)%k) flag=0;
if(tot%k==0){ans=gcd(ans,tot);return 1;}
else{ans=gcd(ans,tot-1);return 0;}
}
// ---------- ---------- //
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
rd(T);
while(T--){
rd(n);
for(int i=1,x,y;i<n;++i){
rd(x);rd(y);es[x].pb(y);es[y].pb(x);
}
int maxn=n-1;
for(re i=2;i*i<=maxn;++i)
if(maxn%i==0){
flag=1;ans=0;k=i;dfs(1,0);
if(flag) a[ans]=1;
while(maxn%i==0) maxn/=i;
}
if(maxn>1){
flag=1;ans=0;k=maxn;dfs(1,0);
if(flag) a[ans]=1;
}
a[1]=pw(2,n-1);
for(re i=2;i<=n;++i) a[1]=(a[1]-a[i]+mod)%mod;
for(re i=1;i<=n;++i) wr(a[i]),putchar(' ');puts("");
for(re i=1;i<=n;++i) a[i]=0,es[i].clear();
}
return 0;
}
// ---------- Main ---------- //