JZOJ6438. 【GDOI2020模擬01.16】樹上的鼠

Description

在這裏插入圖片描述
n<=1e6n<=1e6

Solution

  • 首先考慮簡單的情況——一條鏈。
  • 假設鏈是奇數長度,如果根在鏈的中點,那麼先手的不管怎麼移,後手的都可以移到它的對稱點。反之根不在鏈的中點,先手的就可以移到中點,獲得主動權。
  • 假設鏈長度偶數,那麼可以移到遠離當前點的兩個中點之一,那麼接下來的策略也之前的情況是一樣的。
  • 那麼當且僅當鏈長度爲奇數,且根在中點時先手必敗。
  • 推廣到一顆樹的情況。
  • 可以發現將直徑當作上面的鏈,所有情況都是類似的。
  • 那麼當且僅當直徑長度爲奇數,且根在中點時先手必敗。
  • 然後就可以愉快的樹形DP了,設f[x][i]f[x][i]表示x的子樹最深點深度爲ii的方案數。
  • 因爲只與深度有關,用長鏈剖分即可。
  • 合併的時候f[x][i]=sum[y][i1]f[x][i]+sum[x][i1]f[y][i]+f[x][i]f[y][i]f'[x][i]=sum[y][i-1]*f[x][i]+sum[x][i-1]*f[y][i]+f[x][i]*f[y][i]
  • 考慮對於長度比較長的部分的影響相當於是一個後綴乘一個數的形式,打個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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章