HDU 2481 Toy(Burnside引理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2481

题意:外面有一圈N个结点,中心有一个结点与N个结点都相连,总共就是2*N条边,删除N条边,使N+1个点连通,旋转相同视为等价,问有多少种情况。

思路:源地址:http://blog.csdn.net/acm_cxlove/article/details/7868589 首先计算外圈有n个点有多少种可能,然后再考虑旋转。这里任意取两个结点a、b讨论。那么总数便是a,b断开的种数与a,b连在一起的种数的和(这种分析方法很重要)。f(n)表示外圈有n个结点时,而a,b是断开的种数;g(n)表示外圈有n个结点时,而a,b是连在一起的种数。

(1)如果a,b之间是断开的(如下面左侧的图),如果与a直接相连的为k个(加上a自己),那么显然这k个要与其它的保持连通的,与中心必须有一条边,如果有多条边就形成环了,显然不满足生成树。另外n-k为f[n-k]种,因为这n-k种肯定是断开的。枚举k,则f[n]=sigma(k*f[n-k])(1<=k<=n)。

(2)如果a,b是连在一起的(如上面右侧的图),如果与a,b相连的为k个(包括a,b),那么a,b是相邻的在这k个位置选择就有k-1种,而这k个与中心相连的选择有k种,剩下的与这部分是分开的,则为f[n-k],所以可以枚举k,最终结果g[n]=sigma((k-1)*k*f[n-k])(2<=k<=n)。

最终的种数便是T[n]=f[n]+g[n]。

由f[n]=sigma(k*f[n-k])(1<=k<=n)得到:

f[n]=1*f(n-1)+2*f(n-2)+3*f(n-3)+……+(n-1)*f(1)+n*f[0]

    =s[n-1]+1*f(n-2)+2*f(n-3)+……+(n-2)*f(1)+(n-1)*f[0]

    =s[n-1]+f[n-1]    
    =s[n-2]+f[n-1]+f[n-1]
    =s[n-2]+2*f[n-1]
    =f[n-1]-f[n-2]+2*f[n-1]  
    =3*f[n-1]-f[n-2]    其中f[0]=1,f[1]=1,f[2]=3,f[3]=8

g[n]  =sigama((k-1)*k*f[n-k]) (2<=k<=n)  从这里可以得到g[1]=0
      =1*2*f[n-2]+2*3*f[n-3]+3*4*f[n-4]+……+(n-1)*(n-2)*f[1]+n*(n-1)*f[0]
g[n-1]=           1*2*f[n-3]+2*3*f[n-4]+……+(n-2)*(n-3)*f[1]+(n-1)*(n-2)*f[0]
所以:g(n)-g(n-1)=2*f[n-2]+4*f[n-3]……(2*(n-2))*f[1]+2*(n-1)*f[0]
                 =2*(1*f[n-2]+2*f[n-3]+……+(n-2)*f[1]+(n-1)*f[0])
                 =2*f[n-1]
进而得到:g[n]=2*(f[1]+f[2]+f[3]……f[n-1])+g[1]=2*(s[n-1]-f[0])+g[1]
              =2*(f[n]-f[n-1]-1)+g[1]    

              =2*(f[n]-f[n-1]-1) 其中f[0]=1,g[1]=0
对于f[n]的求法,可以用矩阵快速幂乘解决:定义矩阵A

则(f[n],f[n-1])=(f[1],f[0])*A^(n-1)。而g[n]也就可以顺便得到,T[n]就处理完毕了。

然后就是Burnside定理,同样N比较大,肯定是要用欧拉函数优化,枚举循环个数。n对MOD是可能没有逆元的,用(a/b)%c=(a%(b*c))/b。这样的话把取模就变成了MOD*N,范围到了10^18,这样子的话中间的乘法便会溢出64位整数。乘法二分。

const int N=2;
const int M=40000;
int n;
i64 mod;

class Matrix
{
public:
    i64 a[N][N];

    Matrix()
    {
        clr(a,0);
    }

    void init(int x)
    {
        if(x==0) a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;
        else if(x==1) a[0][0]=a[1][1]=1,a[0][1]=a[1][0]=0;
    }
};

i64 mul(i64 a,i64 b)
{
    a=(a%mod+mod)%mod;
    b=(b%mod+mod)%mod;
    i64 ans=0;
    while(b)
    {
        if(b&1) ans=(ans+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ans;
}

Matrix operator*(Matrix &a,Matrix &b)
{
    int i,j,k;
    Matrix p;
    p.init(0);
    FOR0(i,2) FOR0(j,2) FOR0(k,2)
    {
        (p.a[i][j]+=mul(a.a[i][k],b.a[k][j])%mod)%=mod;
    }
    return p;
}

Matrix operator^(Matrix a,int t)
{
    Matrix ans;
    ans.init(1);
    while(t)
    {
        if(t&1) ans=ans*a;
        a=a*a;
        t>>=1;
    }
    return ans;
}

Matrix a,b;
int prime[M],tag[M],cnt;
vector<int> factor;


void init()
{
    int i,j;
    for(i=2;i<180;i++) if(!tag[i])
    {
        for(j=i*i;j<M;j+=i) tag[j]=1;
    }
    for(i=2;i<M;i++) if(!tag[i])
    {
        prime[++cnt]=i;
    }
}

int eular(int n)
{
    int ans=n,i;
    for(i=1;i<=cnt&&(i64)prime[i]*prime[i]<=n;i++) if(n%prime[i]==0)
    {
        ans=ans/prime[i]*(prime[i]-1);
        while(n%prime[i]==0) n/=prime[i];
    }
    if(n>1) ans=ans/n*(n-1);
    return ans;
}

i64 get(int n)
{
    if(n==1) return 1;
    if(n==2) return 5;
    a=b^(n-2);
    i64 f=3*a.a[0][0]+a.a[1][0];
    i64 g=2*(f-(3*a.a[0][1]+a.a[1][1])-1);
    return (g+f)%mod;
}

int main()
{
    init();
    b.a[0][0]=3; b.a[0][1]=1; b.a[1][0]=-1; b.a[1][1]=0;
    while(scanf("%d%I64d",&n,&mod)!=-1)
    {
        mod*=n;
        i64 ans=0;
        int i;
        for(i=1;i*i<=n;i++) if(n%i==0)
        {
            (ans+=mul(eular(i),get(n/i))%mod)%=mod;
            if(i*i==n) continue;
            (ans+=mul(eular(n/i),get(i))%mod)%=mod;
        }
        ans=ans/n%(mod/n);
        printf("%I64d\n",ans);
    }
    return 0;
}

  

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值