Description
Solution
- 首先考虑简单的情况——一条链。
- 假设链是奇数长度,如果根在链的中点,那么先手的不管怎么移,后手的都可以移到它的对称点。反之根不在链的中点,先手的就可以移到中点,获得主动权。
- 假设链长度偶数,那么可以移到远离当前点的两个中点之一,那么接下来的策略也之前的情况是一样的。
- 那么当且仅当链长度为奇数,且根在中点时先手必败。
- 推广到一颗树的情况。
- 可以发现将直径当作上面的链,所有情况都是类似的。
- 那么当且仅当直径长度为奇数,且根在中点时先手必败。
- 然后就可以愉快的树形DP了,设表示x的子树最深点深度为的方案数。
- 因为只与深度有关,用长链剖分即可。
- 合并的时候
- 考虑对于长度比较长的部分的影响相当于是一个后缀乘一个数的形式,打个tag即可。
- 还要维护前缀和。
- 最后在根节点的时候是类似的。
- OJ上爆栈60分
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 1000005
#define ll long long
#define mo 998244353
using namespace std;
int n,i,j,k,x,y;
int em,e[maxn*2],nx[maxn*2],ls[maxn];
int tot,dfn[maxn],gs[maxn],dep[maxn],mxd[maxn];
ll f[maxn],g[maxn],h[maxn],s[maxn],c[maxn];
void read(int &x){
x=0; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}
void insert(int x,int y){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}
void DFS1(int x,int p){
dep[x]=dep[p]+1,mxd[x]=dep[x],g[x]=1;
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
DFS1(e[i],x),mxd[x]=max(mxd[x],mxd[e[i]]);
if (!gs[x]||mxd[e[i]]>mxd[gs[x]])
gs[x]=e[i];
g[x]=g[x]*(g[e[i]]+1)%mo;
}
}
void DFS2(int x,int p){
dfn[x]=++tot;
if (gs[x]) DFS2(gs[x],x);
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&e[i]!=gs[x])
DFS2(e[i],x);
}
void down(int x,int lim){
if (h[x]!=1){
f[x]=f[x]*h[x]%mo;
if (x<lim) h[x+1]=h[x+1]*h[x]%mo;
h[x]=1;
}
}
void DFS3(int x,int p){
f[dfn[x]]=h[dfn[x]]=s[dfn[x]]=1;
if (gs[x]) DFS3(gs[x],x);
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p&&e[i]!=gs[x]){
DFS3(e[i],x);
int y=e[i];
for(int j=0;j<=mxd[y]-dep[y];j++){ int k=j+1;
down(dfn[y]+j,dfn[y]+mxd[y]-dep[y]),s[dfn[y]+j]=((j>0)*s[dfn[y]+j-1]+f[dfn[y]+j])%mo;
if (x==1) continue;
down(dfn[x]+k,dfn[x]+mxd[x]-dep[x]),s[dfn[x]+k]=(s[dfn[x]+k-1]+f[dfn[x]+k])%mo;
f[dfn[x]+k]=f[dfn[x]+k]*((j>0)*s[dfn[y]+j-1]+1)%mo
+f[dfn[y]+j]*s[dfn[x]+k-1]%mo
+f[dfn[x]+k]*f[dfn[y]+j]%mo;
f[dfn[x]+k]%=mo;
}
if (x==1) continue;
if (mxd[y]<mxd[x])
(h[dfn[x]+mxd[y]-dep[x]+1]*=(s[dfn[y]+mxd[y]-dep[y]]+1))%=mo;
}
}
int to[maxn],tmp[maxn];
void Getans(){
for(i=2;i<=mxd[1];i++)
down(i,mxd[1]),s[i]=(i>2)*s[i-1]+f[i];
ll ans=1;
for(i=0;i<=n;i++) c[i]=1,h[i]=1;
for(i=ls[1];i;i=nx[i]) to[++to[0]]=e[i];
for(i=1;i<=to[0];i++){ int x=to[i];
for(j=2;j<=mxd[x];j++) {
if (h[j]!=1) c[j]=c[j]*h[j]%mo,h[j+1]=h[j+1]*h[j]%mo,h[j]=1;
tmp[dfn[x]+j-2]=c[j-1];
}
for(j=2;j<=mxd[x];j++) c[j]=c[j]*(s[dfn[x]+j-2]+1)%mo;
if (mxd[x]<n) (h[mxd[x]+1]*=(s[dfn[x]+mxd[x]-2]+1))%=mo;
}
for(i=0;i<=n;i++) c[i]=1,h[i]=1;
for(i=to[0];i;i--){ int x=to[i];
for(j=2;j<=mxd[x];j++) {
if (h[j]!=1) c[j]=c[j]*h[j]%mo,h[j+1]=h[j+1]*h[j]%mo,h[j]=1;
ans+=tmp[dfn[x]+j-2]*f[dfn[x]+j-2]%mo*(c[j]-c[j-1])%mo;
}
for(j=2;j<=mxd[x];j++) c[j]=c[j]*(s[dfn[x]+j-2]+1)%mo;
if (mxd[x]<n) (h[mxd[x]+1]*=(s[dfn[x]+mxd[x]-2]+1))%=mo;
}
printf("%lld",((g[1]-ans)%mo+mo)%mo);
}
int main(){
read(n);
for(i=1;i<n;i++) read(x),read(y),insert(x,y);
DFS1(1,0);
DFS2(1,0);
DFS3(1,0);
Getans();
}