HDU2481 Toy【Burnside + 生成函数求递推式】

题目描述:

一个n+1个点,2n条边的图如下:
在这里插入图片描述
现在要删除n条边使其成为一棵树,问有多少种不同的方案数,旋转算相同。
答案mod m,n,m<=109

题目分析:

首先Burnside一波转化为求一个置换下的不动点数,再枚举gcd变成求长度为 k k k的环加上中间的点的生成树个数。
在这里插入图片描述
分为两种情况:

  • 如果1和 k ′ k' k没有连通,设 f [ i ] f[i] f[i]表示前 i i i个点构成生成树的方案数。枚举在哪个点断开进行转移,得到 f [ i ] = ∑ j = 0 i − 1 f [ j ] ∗ ( i − j ) f[i]=\sum_{j=0}^{i-1}f[j]*(i-j) f[i]=j=0i1f[j](ij)
    但是显然这个式子没法矩阵加速,但是它的形式又这么优美,我们尝试把它写成一个封闭形式,记 f f f的生成函数为 F ( x ) = ∑ i = 0 ∞ f i ∗ x i F(x)=\sum_{i=0}^{\infty}f_i*x^i F(x)=i=0fixi.
    递推式是一个卷积形式,设 G ( x ) = ∑ i = 0 ∞ i ∗ x i G(x)=\sum_{i=0}^{\infty}i*x^i G(x)=i=0ixi,得到:
    F ( x ) = F ( x ) ∗ G ( x ) + 1 F(x)=F(x)*G(x)+1 F(x)=F(x)G(x)+1
    + 1 +1 +1是因为 f [ 0 ] = 1 f[0]=1 f[0]=1
    移项得: F ( x ) = 1 1 − G ( x ) F(x)=\frac 1{1-G(x)} F(x)=1G(x)1
    G ( x ) = ∑ i = 0 ∞ i ∗ x i = x ∗ ∑ i = 1 ∞ i ∗ x i − 1 = x ∗ ( 1 1 − x ) ′ = x ( 1 − x ) 2 G(x)=\sum_{i=0}^{\infty}i*x^i=x*\sum_{i=1}^{\infty}i*x^{i-1}=x*(\frac 1{1-x})'=\frac x{(1-x)^2} G(x)=i=0ixi=xi=1ixi1=x(1x1)=(1x)2x
    带入化简得到 F ( x ) = x 2 − 2 x + 1 x 2 − 3 x + 1 F(x)=\frac {x^2-2x+1}{x^2-3x+1} F(x)=x23x+1x22x+1,移项得
    F ( x ) − 3 x F ( x ) + x 2 F ( x ) = x 2 − 2 x + 1 F(x)-3xF(x)+x^2F(x)=x^2-2x+1 F(x)3xF(x)+x2F(x)=x22x+1
    由左右两边 x i x^i xi对应系数相等,得到:
    f n − 3 ∗ f n − 1 + f n − 2 = 0    ( n ≥ 3 ) f 2 − 3 ∗ f 1 + f 0 = 1 f 1 − 3 ∗ f 0 = − 2 f 0 = 1 \begin{aligned}f_n-3*f_{n-1}+f_{n-2}&=0~~(n\ge 3)\\ f_2-3*f_1+f_0&=1\\ f_1-3*f_0&=-2\\ f_0&=1 \end{aligned} fn3fn1+fn2f23f1+f0f13f0f0=0  (n3)=1=2=1
    可得 f 0 = 1 , f 1 = 1 , f 2 = 3 , f 3 = 3 f 2 − f 1 = 8 … f_0=1,f_1=1,f_2=3,f_3=3f_2-f_1=8\dots f0=1,f1=1,f2=3,f3=3f2f1=8
    然后矩阵加速即可。

  • 如果1和 k ′ k' k连通,那么还要在 1 1 1 k k k之间找两条边(可以相同)断开,枚举1所在的连通块的长度 i i i,中间还剩 k − i k-i ki个点方案为 f [ k − i ] f[k-i] f[ki]。所以 g k = ∑ i = 2 k i ∗ ( i − 1 ) ∗ f [ k − i ] g_k=\sum_{i=2}^ki* (i-1)*f[k-i] gk=i=2ki(i1)f[ki],设g的生成函数为 P ( x ) P(x) P(x)

    同理可以配一个 H ( x ) = ∑ i = 2 ∞ i ∗ ( i − 1 ) x i = ( 1 1 − x ) ′ ′ x 2 H(x)=\sum_{i=2}^{\infty}i*(i-1)x^i=(\frac 1{1-x})''x^2 H(x)=i=2i(i1)xi=(1x1)x2
    那么 P ( x ) = H ( x ) ∗ F ( x ) = 2 x 2 ( 1 − x ) 3 ∗ x 2 − 2 x + 1 x 2 − 3 x + 1 = 2 x 2 ( 1 − x ) ( x 2 − 3 x + 1 ) P(x)=H(x)*F(x)=\frac {2x^2}{(1-x)^3}*\frac {x^2-2x+1}{x^2-3x+1}=\frac {2x^2}{(1-x)(x^2-3x+1)} P(x)=H(x)F(x)=(1x)32x2x23x+1x22x+1=(1x)(x23x+1)2x2
    移项得 ( 1 − 4 x + 4 x 2 − x 3 ) P ( x ) = 2 x 2 (1-4x+4x^2-x^3)P(x)=2x^2 (14x+4x2x3)P(x)=2x2
    同样,对比系数得到:
    g n − 4 ∗ g n − 1 + 4 ∗ g n − 2 − g n − 3 = 0    ( n ≥ 3 ) g 2 − g 1 + 4 ∗ g 0 = 2 g 1 − 4 ∗ g 0 = 0 g 0 = 0 \begin{aligned}g_n-4*g_{n-1}+4*g_{n-2}-g_{n-3}&=0~~(n\ge 3)\\ g_2-g_1+4*g_0&=2\\ g_1-4*g_0&=0\\ g_0&=0 \end{aligned} gn4gn1+4gn2gn3g2g1+4g0g14g0g0=0  (n3)=2=0=0
    可得 g 0 = 0 , g 1 = 0 , g 2 = 2 , g 3 = 4 g 2 − 4 g 1 + g 0 = 8 … g_0=0,g_1=0,g_2=2,g_3=4g_2-4g_1+g_0=8\dots g0=0,g1=0,g2=2,g3=4g24g1+g0=8
    矩阵加速即可。

最后的答案就是 f k + g k f_k+g_k fk+gk。由于Burnside最后要除 n n n,可能没有逆元,可以用 a / b % c = a % ( b c ) / b a/b\%c=a\%(bc)/b a/b%c=a%(bc)/b,令 m o d    = n ∗ m \mod=n*m mod=nm,中间的乘法要用大整数快速乘。

另外,此题也有其他求解递推式的方法,例如利用前缀和以及错位相消,以及利用矩阵数定理直接求行列式,再求行列式的递推式

Code:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,m,p[3005],d[3005],cnt;
LL mod,ans;
inline LL mul(LL a,LL b){LL r=a*b-(LL)((long double)a/mod*b+0.5)*mod;return r<0?r+mod:r;}
struct Mat{
    LL s[3][3];int o;
    Mat(){memset(s,0,sizeof s);}
    void reset(int x){
        if(!x) s[0][0]=3,s[0][1]=1,s[1][0]=s[1][1]=0,o=2;
        else if(x==1) s[0][0]=3,s[1][0]=-1,s[0][1]=1,s[1][1]=0,o=2;
        else if(x==2) memset(s,0,sizeof s),s[0][0]=2,o=3;
        else memset(s,0,sizeof s),s[0][0]=4,s[1][0]=-4,s[2][0]=1,s[0][1]=s[1][2]=1,o=3;
    }
    Mat operator * (const Mat &B)const{
        Mat ret;ret.o=o;
        for(int k=0;k<o;k++) for(int i=0;i<o;i++) for(int j=0;j<o;j++)
            ret.s[i][j]=(ret.s[i][j]+mul(s[i][k],B.s[k][j]))%mod;
        return ret;
    }
}f,g;
LL calc(int N){
    if(N<=1) return 1;
    int tmp=N;
    for(N-=2,f.reset(0),g.reset(1);N;N>>=1,g=g*g) if(N&1) f=f*g;
    LL ret=f.s[0][0]; N=tmp;
    for(N-=2,f.reset(2),g.reset(3);N;N>>=1,g=g*g) if(N&1) f=f*g;
    return (ret+f.s[0][0]+2*mod)%mod;
}
void dfs(int i,int x,int phi){
    if(i>cnt) {ans=(ans+mul(phi,calc(n/x)))%mod;return;}
    for(int j=0;j<=d[i];j++) dfs(i+1,x,phi),x*=p[i],phi*=p[i]-bool(!j);
}
int main()
{
    while(~scanf("%d%d",&n,&m)){
        mod=1ll*n*m,ans=cnt=0; int x=n;
        for(int i=2;i*i<=x;i++) if(x%i==0) {p[++cnt]=i,d[cnt]=0;while(x%i==0) x/=i,d[cnt]++;}
        if(x>1) p[++cnt]=x,d[cnt]=1;
        dfs(1,1,1);
        printf("%lld\n",ans/n);
    }
}

U p d : Upd: Upd: g k g_k gk其实可以看做在中间断一条边,然后可以左边向环中央那个点连边,右边不动,方案为 ∑ i = 1 k − 1 f [ i ] \sum_{i=1}^{k-1}f[i] i=1k1f[i],右边连边同理。所以最后的答案就是 2 ∗ ∑ i = 1 k − 1 f [ i ] + f [ k ] 2*\sum_{i=1}^{k-1}f[i]+f[k] 2i=1k1f[i]+f[k],对 f f f矩阵加速的时候带上一个前缀和即可。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值