WC2019 tree

WC2019唯一一道正常的题,考场上没什么想法,也只拿到了暴力分。搞了一天终于做完了。

前置知识:purfer序,多项式exp或分治FTT。

对于\(type=0\)的,随便维护下,算下联通块即可。

对于\(type=1\)的,如果有\(k\)个联通块,贡献就是\(y^k\),等价于\((y-1+1)^k\),等价于\(\sum_{i=0}^{k}{k \choose i}(y-1)^i\),就是拆出一个\(1\),然后二项式展开。

原本的贡献是\(y^n\)现在每有一条边同时出现在两颗树上,那么贡献就要乘上\(y^{-1}\)

从此以后题道的所有的\(y\)都是\(y^{-1}\)

这样有什么用呢?这个式子的含义是枚举\(n​\)个点的所有边的子集\(E​\),如果\(E​\)同时是两棵树的边的子集,那么贡献就是\((y-1)^{|E|}​\),否则是\(0​\)。这样组和数已经算在里面了,每个子集的贡献最后合成了一个方案的贡献。

现在我们已知第一颗树,我们钦定一些边是两颗树上都有的边,并且假设这些边组成的联通块把数分成了\(m\)个联通块,那么这棵树的方案数为:

\(\sum_{\sum_{i=1}^{m}d_i==2(m-1)}(m-2)! \prod \frac{a_i^{d_i}}{(d_i-1)!}\)

\(d_i​\)是度数,\(a_i​\)是联通块里点的个数。

\[\frac{(m-2)!}{\prod (d_i-1)!}$$就是组合数选点放在$prufer$序里。$a_i^{d_i}$就是枚举每个从这个联通块连出去的边连在那个点上。 把$d_i​$换成$d_i-1​$,式子变成: ### $\sum_{\sum_{i=1}^{m}d_i==m-2}(m-2)! \prod \frac{a_i^{d_i+1}}{d_i!}$ 等价于: ### $n^{m-2}\prod_{i=1}^{m}a_i$ 现在要证明 ### $\sum_{\sum_{i=1}^{m}d_i==m-2}(m-2)! \prod \frac{a_i^{d_i}}{d_i!}==n^{m-2}$ 现在我们把$d_i​$看成物品,除了组合选点放在$prufer​$序中,$d_i​$还要乘上$a_i​$。现在你直接考虑每一个$d_i​$,它的贡献。其实他们直接互补影响,所以一个$d_i​$的贡献可以是任意一个$a_i​$,所以总贡献就是${\sum a_i}^{m-2}​$,也就是$n^{m-2}​$。 现在我们只需要算出所有方案的贡献和即可。但是我们没有办法记录$a_i$,所以有一个巧妙的转化,在一个大小为$a_i$的联通块内选一个点的方案数也是$a_i$,所以我们只需要设$dp[u][0/1]$,表示$u$及$u$的子树的贡献和,其中$u$所在的联通块是否选择了一个点即可,选择一条边在边集里的贡献是$(y-1)$,不在的话意味着产生了一个新的联通块,贡献为$n$。 对于$ty=2​$的,我们两棵树都不知道,所以我们直接假设至少有有$l​$条边在两棵树中,令$m=n-l​$就是有$l​$个联通块。 ### $\sum_{\sum_{i=1}^{m}a_i ==n} \frac{n!\prod_{i=1}^{m}\frac{a_i^{a_i-2}}{a_i!}}{m!}*(n^{m-2}\prod a_i)^2$ 枚举每个联通块里有多少个点,$a_i^{a_i-2}$就是每个联通块里都自己构成一棵树,下面除掉$m!$是因为联通块之间无顺序,后面就是两棵树的方案。 带上容斥系数 ### $\sum_{\sum_{i=1}^{m}a_i==n-2}(y-1)^{n-m}n^{2(m-2)}\prod_{i=1}^{m}{(\sum_{j=1}^{i}a_j)-1\choose a_i-1}a_i^{a_i}$ 大多数都是直接从上面的式子化出来的,只有一个地方: ### $\frac{n!}{m!\prod a_i!}=\prod_{i=1}^{m}{(\sum_{j=1}^{i}a_j)-1\choose a_i-1}$ 右边的意思就是每次从当前所有点数中取出$a_i$个,上下都要减1是因为是无序的,为了保证不算重,要强制某个特殊的点在这个联通块里,比如编号最小的那个。 考虑动态规划,设$f[k]$为$k$个点的贡献之和,答案就是$f[n]$。 初始化$f[0]=(y-1)^n*n!*n^{-4}$,但是真正用生成函数时这些东西要最后乘上去。 转移很简单,就是枚举最后一个联通块的大小。 ### $f[k]=(y-1)^{-1}*n^2*\sum_{i=1}^{k}{k-1 \choose i-1}i^i*f[k-i]$ ### $=(y-1)^{-1}*n^2*\sum_{i=1}^{k}\frac{(k-1)!*i^i}{(i-1)!(k-i)!}f[k-i]$ 两边同除$k!$,这样我们求的东西从$f[k]$变成了$\frac{f[k]}{k!}$,最后在乘回来即可,后面的$f[k]$均为$\frac{f[k]}{k!}$。 ### $f[k]=(y-1)^{-1}*n^2*\sum_{i=1}^{k}\frac{f[k-i]*i^i}{k*(i-1)!}$ 好像可以直接分治$FFT​$了。 我们令$f=f[i]x^i$,$g=\frac{i^i}{(i-1)!}*(y-1)^{-1}*n^2$。 因为右边等式下面有个$\frac{1}{k}$,我们把$k$乘到右边去,但是这样第i项的系数就到第$i+1$项去了,为了满足此递推式,我们要求导。 ### $f'x=fg$ ### $\frac{f'}{f}=\frac{g}{x}$ ### $ln(f)=\int \frac{g}{x} dx$ 然后你在推一推,发现$\int \frac{g}{x} dx=\sum n^2*(y-1)^{-1}*\frac{i^i}{i!}$。 直接对这个多项式$exp$即可。 ```cpp #include<bits/stdc++.h> using namespace std; typedef int sign; typedef long long ll; #define For(i,a,b) for(register sign i=(sign)(a);i<=(sign)(b);++i) #define Fordown(i,a,b) for(register sign i=(sign)(a);i>=(sign)(b);--i) template<typename T>bool cmax(T &a,T b){return (a<b)?a=b,1:0;} template<typename T>bool cmin(T &a,T b){return (a>b)?a=b,1:0;} template<typename T>T read() { T ans=0,f=1; char ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')f=-1,ch=getchar(); while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-'0'),ch=getchar(); return ans*f; } template<typename T>void write(T x,char y) { if(x==0) { putchar('0'),putchar(y); return; } if(x<0) { putchar('-'); x=-x; } static char wr[20]; int top=0; for(;x;x/=10)wr[++top]=x%10+'0'; while(top)putchar(wr[top--]); putchar(y); } void file() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); } const int N=1e5+5; int n,m,ty; void input() { n=read<int>(),m=read<int>(),ty=read<int>(); } const int mo=998244353; int power(int x,int y) { int res=1; for(;y;x=1ll*x*x%mo,y>>=1)if(y&1)res=1ll*res*x%mo; return res; } namespace sub1 { vector<int>E[N]; unordered_map<int,bool>mp[N]; int fa[N]; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} void solve() { int x,y; For(i,2,n) { x=read<int>(),y=read<int>(); E[x].push_back(y); E[y].push_back(x); } For(i,2,n) { x=read<int>(),y=read<int>(); mp[x][y]=1,mp[y][x]=1; } For(i,1,n)fa[i]=i; For(u,1,n)for(int v:E[u]) { if(mp[u][v])fa[find(v)]=find(u); } int cnt=0; For(i,1,n)if(i==fa[i])cnt++; write(power(m,cnt),'\n'); } } int dp0,dp1; void add(int &x,int y){x+=y;x-=(x>=mo?mo:0);} namespace sub2 { int invm; vector<int>E[N]; int dp[N][2]; void DP(int u,int pre) { dp[u][0]=dp[u][1]=1; for(int v:E[u])if(v^pre) { DP(v,u); dp0=dp1=0; add(dp0,1ll*dp[u][0]*dp[v][0]%mo*(invm-1)%mo); add(dp0,1ll*dp[u][0]*dp[v][1]%mo*n%mo); //add(dp1,1ll*dp[u][0]*dp[v][0]%mo); add(dp1,1ll*dp[u][1]*dp[v][0]%mo*(invm-1)%mo); add(dp1,1ll*dp[u][0]*dp[v][1]%mo*(invm-1)%mo); add(dp1,1ll*dp[u][1]*dp[v][1]%mo*n%mo); dp[u][0]=dp0; dp[u][1]=dp1; } } void solve() { int x,y; For(i,2,n) { x=read<int>(),y=read<int>(); E[x].push_back(y); E[y].push_back(x); } invm=power(m,mo-2); DP(1,0); write(1ll*dp[1][1]*power(n,mo-2)%mo*power(m,n)%mo,'\n'); } } const int max_log=22; int Mod(int x){return x>=mo?x-mo:x;} namespace NTT { int f[1<<max_log],g[1<<max_log]; void init(int n) { int len=1; for(;len<=n;len<<=1); for(int i=2;i<=len;i<<=1) { f[i]=power(3,(mo-1)/i); g[i]=power(f[i],mo-2); } } int rev[1<<max_log]; #define rg register void NTT(int *p,int len,int type) { static int u,v,tim,wn,x; For(i,1,len-1)if(i<rev[i])swap(p[i],p[rev[i]]); for(rg int i=2;i<=len;i<<=1) { tim=i>>1; wn=(type==1?f[i]:g[i]); assert(wn); for(rg int j=0;j<len;j+=i) { x=1; for(rg int k=0;k<tim;++k,x=1ll*x*wn%mo) { u=p[j+k],v=1ll*p[j+k+tim]*x%mo; p[j+k]=Mod(u+v); p[j+k+tim]=Mod(u-v+mo); } } } if(type==-1) { int inv=power(len,mo-2); For(i,0,len-1)p[i]=1ll*p[i]*inv%mo; } } int A[1<<max_log],B[1<<max_log],C[1<<max_log]; void get_rev(int len,int cnt) { For(i,1,len-1)rev[i]=rev[i>>1]>>1|((i&1)<<(cnt-1)); } void mul(int *a,int *b,int *c,int n1,int n2) { int len,cnt; for(len=1,cnt=0;len<=n1+n2;len<<=1)cnt++; get_rev(len,cnt); For(i,0,n1)A[i]=a[i]; For(i,0,n2)B[i]=b[i]; NTT(A,len,1),NTT(B,len,1); For(i,0,len)C[i]=1ll*A[i]*B[i]%mo; NTT(C,len,-1); For(i,0,n1+n2)c[i]=C[i]; For(i,0,len)A[i]=B[i]=0; } } namespace INV { int C[1<<max_log],D[1<<max_log]; void get_inv(int *A,int *B,int len,int cnt) { if(len==1){B[0]=power(A[0],mo-2);return;} get_inv(A,B,len>>1,cnt-1); NTT::get_rev(len<<1,cnt+1); For(i,0,len-1)C[i]=A[i],D[i]=B[i]; NTT::NTT(C,len<<1,1); NTT::NTT(D,len<<1,1); For(i,0,(len<<1)-1)C[i]=1ll*D[i]*D[i]%mo*C[i]%mo; NTT::NTT(C,len<<1,-1); For(i,len>>1,len-1)B[i]=Mod(Mod(B[i]+B[i])-C[i]+mo); For(i,0,(len<<1)-1)C[i]=D[i]=0; } int A[1<<max_log],B[1<<max_log]; void inv(int *a,int *b,int n) { int len=1,cnt=0; for(;len<=n;len<<=1)cnt++; For(i,0,len<<1)A[i]=B[i]=C[i]=D[i]=0; For(i,0,n)A[i]=a[i]; get_inv(A,B,len,cnt); For(i,0,n)b[i]=B[i]; } } namespace LN { int A[1<<max_log],B[1<<max_log],C[1<<max_log]; void Direv(int *a,int *b,int n) { For(i,1,n)b[i-1]=1ll*a[i]*i%mo; } int inv[1<<max_log],ans[1<<max_log]; void Inter(int *a,int *b,int n) { inv[1]=1; For(i,1,n) { if(i>1)inv[i]=1ll*inv[mo%i]*(mo-mo/i)%mo; b[i]=1ll*a[i-1]*inv[i]%mo; } } void get_ln(int *a,int *b,int n) { Direv(a,B,n); INV::inv(a,C,n); NTT::mul(B,C,A,n-1,n); Inter(A,ans,n); For(i,0,n)b[i]=ans[i]; } } namespace EXP { int A[1<<max_log],B[1<<max_log],C[1<<max_log],D[1<<max_log]; void exp(int *a,int *b,int n) { For(i,0,n)A[i]=a[i]; B[0]=1; int len=2,cnt=1; while(len<=(n<<1)) { For(i,0,(len>>1)-1)C[i]=D[i]=B[i]; LN::get_ln(C,D,len-1); For(i,0,len-1)D[i]=Mod(A[i]-D[i]+mo); D[0]=Mod(D[0]+1); NTT::get_rev(len<<1,cnt+1); NTT::NTT(C,len<<1,1); NTT::NTT(D,len<<1,1); For(i,0,(len<<1)-1)C[i]=1ll*C[i]*D[i]%mo; NTT::NTT(C,len<<1,-1); For(i,0,len-1)B[i]=C[i]; For(i,0,len<<1)C[i]=D[i]=0; len<<=1,cnt++; } For(i,0,n)b[i]=B[i]; } } namespace sub3 { int f[1<<max_log],g[1<<max_log]; int mc[N],inv[N]; void solve() { if(m==1){write(power(power(n,n-2),2),'\n');return;} NTT::init(n<<2); int invm=power(m,mo-2); mc[0]=inv[0]=1; For(i,1,n)mc[i]=1ll*mc[i-1]*i%mo; inv[n]=power(mc[n],mo-2); Fordown(i,n-1,1)inv[i]=1ll*inv[i+1]*(i+1)%mo; int tmp=power(invm-1,mo-2); int d=1ll*tmp*n%mo*n%mo; For(i,1,n)g[i]=1ll*d*power(i,i)%mo*inv[i]%mo; EXP::exp(g,f,n); int ans=f[n]; //cerr<<ans<<endl; ans=1ll*ans*power(power(n,mo-2),4)%mo*mc[n]%mo*power(m,n)%mo*power(invm-1,n)%mo; write(ans,'\n'); } } void work() { if(ty==0)sub1::solve(); else if(ty==1)sub2::solve(); else if(ty==2)sub3::solve(); } int main() { file(); input(); work(); return 0; } ```\]

posted @ 2019-03-12 15:29  dyx_diversion  阅读(215)  评论(0编辑  收藏  举报