• 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();
    }
    
  • 相关阅读:
    第二类斯特林数学习笔记
    [ZJOI2017]树状数组
    「LibreOJ Round #6」花火
    [Ynoi2016]这是我自己的发明 莫队
    codeforces706E
    扩展CRT
    PKUSC2018游记
    「PKUWC 2018」Minimax
    「SHOI2015」(LOJ2038)超能粒子炮・改
    Codeforces712E
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090891.html
Copyright © 2020-2023  润新知