bzoj 2601: [Jsoi2011]同分异构体计数

Description

Antonio 最近对有机化学比较感兴趣,他想请你帮助他快速计算出某种烃类的同分异

构体的数目。

为了表述方便,我们作出如下定义:

环烷烃: 具有n 个碳原子的环烷烃可以表示成一张具有n 个顶点n 条边的无向连通

简单图(基环+外向树)。每个顶点的度数不超过 4。

M-环烷烃:至多有m 个顶点在环上的环烷烃。(注意环上至少有 3 个顶点,因为

任意两个顶点之间至多只能有1 条边)。

同构:假设结构A和结构B 均具有n 个碳原子,A和B 同构当且仅当能够对A和

B 中的每个碳原子都按照 1~n 编号,使得对于编号为 v1 和 v2 的两个碳原子,他

们在 A中存在边相连当且仅当他们在 B中存在边相连。(换言之,A和 B对应的图

同构)。

现在,给出n, m,Antonio 希望你帮助他统计有多少种互不同构的含有n 个碳原子的

m-环烷烃。由于这个数量可能很大,你只需要输出它对p 的余数。(p是一个素数)。

在本题中,我们不考虑某结构在化学上是否能够稳定存在,也不考虑其他的异构方式。

Input

输入文件只有一行,用空格隔开的三个整数n, m, p 。保证有m <=n,p为素数。

Output

输出文件有且仅有一行,表示具有n 个碳原子的互不同构的m-环烷烃的数量,对 p的

余数。

先处理出根的度为2,其余点度<=4的无标号有根树的方案数

环有旋转和翻转两种变换,由于m>=3,构成的置换群阶为2m,用burnside引理处理

旋转k(0<=k<m)步可以形成gcd(m,k)个等价类,每个等价类包含m/gcd(m,k)个位置

旋转+翻转需要分奇偶处理:

若m为奇数,则有m个这种置换,形成(m+1)/2个等价类,其中一个等价类包含1个位置,其余包含2个位置

若m为偶数

  则有m/2个置换形成m/2个等价类,每个等价类包含2个位置

  另有m/2个置换形成m/2+1个等价类,其中两个等价类包含1个位置,其余包含2个位置

#include<cstdio>
typedef unsigned long long u64;
typedef unsigned int u32;
int n,m;
u32 P;
int gcd(int a,int b){
    for(int c;b;c=a,a=b,b=c%b);
    return a;
}
int phi(int n){
    int v=n;
    for(int i=2;i*i<=n;++i)if(n%i==0){
        do n/=i;while(n%i==0);
        v=v/i*(i-1);
    }
    if(n>1)v=v/n*(n-1);
    return v;
}
inline u32 fix(int a){
    return a+(a>>31&P);
}
struct num{
    u32 x;
    num(u32 a=0):x(a){}
    num operator+(num w){return fix(x+w.x-P);}
    num operator*(num w){return u64(x)*w.x%P;}
    void operator+=(num w){x=fix(x+w.x-P);}
};
num s[5][1007],gs[11],iv[117],f0[57][1007],f1[57][1007],ans;
void cal(int m,int n){
    int g=gcd(n,m);
    num v=0;
    for(int d=1;d<=g;++d)if(g%d==0)v+=f0[m/d][n/d]*phi(d);
    v+=f1[m][n]*m;
//    printf("%d,%d:%d\n",m,n,v*iv[m*2]);
    ans+=v*iv[m*2];
}
int main(){
    scanf("%d%d%u",&n,&m,&P);
    if(m>n)m=n;
    s[0][0]=iv[1]=1;
    for(int i=2;i<=115;++i)iv[i]=iv[P%i]*(P-P/i);
    for(int i=1;i<=n;++i){
        f0[1][i]=f1[1][i]=s[0][i-1]+s[1][i-1]+s[2][i-1];
        gs[1]=f0[1][i]+s[3][i-1];
//        printf("[%d:%d]\n",i,gs[1]);
        for(int j=2;j<=3;++j)gs[j]=gs[j-1]*(gs[1]+(j-1))*iv[j];
        for(int j=3;j;--j){
            for(int k=n;k>=i;--k){
                for(int t=1;t<=j;++t){
                    int w=k-t*i;
                    if(w>=0)s[j][k]+=gs[t]*s[j-t][w];
                }
            }
        }
    }
    for(int i=2;i<=m;++i){
        for(int j=i;j<=n;++j){
            for(int k=1;k<j;++k)f0[i][j]+=f0[i-1][j-k]*f0[1][k];
            if(i&1){
                for(int k=2;k<j;k+=2)f1[i][j]+=f0[i>>1][k>>1]*f0[1][j-k];
            }else{
                for(int k=1;k<j;++k)f1[i][j]+=f1[i-1][j-k]*f0[1][k];
                if(~j&1)f1[i][j]+=f0[i>>1][j>>1];
                f1[i][j]=f1[i][j]*iv[2];
            }
//            printf("%d,%d %d %d\n",i,j,f0[i][j],f1[i][j]);
        }
    }
    for(int i=3;i<=m;++i)cal(i,n);
    printf("%d\n",ans.x);
    return 0;
}
时间: 2024-10-12 22:05:25

bzoj 2601: [Jsoi2011]同分异构体计数的相关文章

BZOJ 2111 Perm 排列计数(满二叉树)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2111 题意:求1到n有多少种排列满足:A[i]>A[i/2](2<=i<=n). 思路:形式类似二叉树.建模之后其实就是n个节点的不同的满二叉树有多少种?用f[i]表示i个节点的满二叉树个数,则f[n]=f[L]*f[R]*C(n-1,L).其中L和R对于确定的n来说是确定的.比如n=10时,左右子树分别有6.3个点. i64 a[N],n,p,f[N]; void init(

BZOJ 4517: [Sdoi2016]排列计数 错排+逆元

4517: [Sdoi2016]排列计数 Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是稳定的 满足条件的序列可能很多,序列数对 10^9+7 取模. Input 第一行一个数 T,表示有 T 组数据. 接下来 T 行,每行两个整数 n.m. T=500000,n≤1000000,m≤1000000 Output 输出 T 行,每行一个数,表示

[BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

题目链接:BZOJ - 1016 题目分析 最小生成树的两个性质: 同一个图的最小生成树,满足: 1)同一种权值的边的个数相等 2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等 这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法. 最后根据乘法原理将各种边的选法数乘起来就可以了. 特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路

BZOJ 2209: [Jsoi2011]括号序列 [splay 括号]

2209: [Jsoi2011]括号序列 Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 1111  Solved: 541[Submit][Status][Discuss] Description Input 输入数据的第一行包含两个整数N和Q,分别表示括号序列的长度,以及操作的个数. 第二行包含一个长度为N的括号序列. 接下来Q行,每行三个整数t.x和y,分别表示操作的类型.操作的开始位置和操作的结 束位置,输入数据保证x不小于y.其中t=0表

数学(错排):BZOJ 4517: [Sdoi2016]排列计数

4517: [Sdoi2016]排列计数 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 693  Solved: 434[Submit][Status][Discuss] Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是稳定的 满足条件的序列可能很多,序列数对 10^9+7 取模. Input 第

bzoj 1004 Cards 组合计数

这道题考察的是组合计数(用Burnside,当然也可以认为是Polya的变形,毕竟Polya是Burnside推导出来的). 这一类问题的本质是计算置换群(A,P)中不动点个数!(所谓不动点,是一个二元组(a,p),a∈A,p∈P ,使得p(a)=a,即a在置换p的作用后还是a). Polya定理其实就是告诉了我们一类问题的不动点数的计算方法. 对于Burnside定理的考察,我见过的有以下几种形式(但归根结底还是计算不动点数): 1.限制a(a∈A)的特点,本题即是如此(限制了各颜色个数,可以

BZOJ 4517: [Sdoi2016]排列计数

4517: [Sdoi2016]排列计数 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 911  Solved: 566[Submit][Status][Discuss] Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是稳定的 满足条件的序列可能很多,序列数对 10^9+7 取模. Input 第

[BZOJ] 2456: mode #众数计数法

2456: mode Time Limit: 1 Sec  Memory Limit: 1 MBSubmit: 5969  Solved: 2414[Submit][Status][Discuss] Description 给你一个n个数的数列,其中某个数出现了超过n div 2次即众数,请你找出那个数. Input 第1行一个正整数n.第2行n个正整数用空格隔开. Output 一行一个正整数表示那个众数. Sample Input 5 3 2 3 1 3 Sample Output 3 HI

bzoj 4401 块的计数 思想+模拟+贪心

块的计数 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 455  Solved: 261[Submit][Status][Discuss] Description 小Y最近从同学那里听说了一个十分牛B的高级数据结构——块状树.听说这种数据结构能在sqrt(N)的时间内维护树上的各种信息,十分的高效.当然,无聊的小Y对这种事情毫无兴趣,只是对把树分块这个操作感到十分好奇.他想,假如能把一棵树分成几块,使得每个块中的点数都相同该有多优美啊!小Y很想知