BZOJ 1815: [Shoi2006]color 有色图 [Polya DFS 重复合并]

传送门

题意:

染色图是无向完全图,且每条边可被染成k种颜色中的一种。
两个染色图是同构的,当且仅当可以改变一个图的顶点的编号,使得两个染色图完全相同。
问N个顶点,k种颜色,本质不同的染色图个数(模质数N≤53,P<109)。



想了一节课和一中午又看了课件

相同类型的循环合并的想法很巧妙

首先,点的置换对应唯一边的置换,我们可以枚举所有点的置换,找出每个置换下边置换的循环有多少个,然后套$Polya$公式

但是复杂度带叹号

我们发现,很多点置换类型是一样的,我们可以对$n$搜索划分来枚举点置换的类型(即每个循环的长度),然后找出这种类型的置换有多少个

设每个循环长度$L_1,L_2,...,L_n$,那么相同类型的置换就相当于每个循环做圆排列,然后消除循环长度相同的影响

$\frac{n!}{L_1 L_2...L_n s_1! s_2!...s_t!}$

$s$为相同的长度的个数

那么如何从点的置换得到边的置换?

同一个循环里的边,他们的置换个数为$\frac{L}{2}$,具体可以把点排成一个圈画图观察一下

两个循环之间的边,他们的置换长度为$LCM(L_1,L_2)$,共有$L_1*L_2$条边,则个数为$GCD(L_1,L_2)$

然后就可以做了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=60;
typedef long long ll;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘; c=getchar();}
    return x*f;
}
int n,m,P;
ll inv[N],fac[N],facInv[N];
void ini(){
    inv[1]=1;fac[0]=facInv[0]=1;
    for(int i=1;i<=n;i++){
        if(i!=1) inv[i]=-P/i*inv[P%i]%P;
        if(inv[i]<0) inv[i]+=P;
        fac[i]=fac[i-1]*i%P;
        facInv[i]=facInv[i-1]*inv[i]%P;
    }
}
int L[N],tot;
ll sum,ans;
inline int gcd(int a,int b){return b==0 ? a : gcd(b,a%b);}
inline ll Pow(ll a,int b){
    ll re=1;
    for(;b;b>>=1,a=a*a%P)
        if(b&1) re=re*a%P;
    return re;
}
inline void mod(ll &x){if(x>=P) x-=P;}
void dfs(int d,int now){
    if(d==n){
        int lo=0;
        ll cnt=fac[n],same=1;
        sort(L+1,L+1+tot);
        //printf("tot %d\n",tot);
        //for(int i=1;i<=tot;i++) printf("%d ",L[i]);puts("\n end");
        for(int i=1;i<=tot;i++){
            lo+=L[i]/2;
            for(int j=i+1;j<=tot;j++) lo+=gcd(L[i],L[j]);

            cnt=cnt*inv[L[i]]%P;
            if(i!=1&&L[i]==L[i-1]) same++;
            else if(same!=1) cnt=cnt*facInv[same]%P,same=1;
        }
        if(same!=1) cnt=cnt*facInv[same]%P;
        //printf("hi %d %lld\n",lo,cnt);
        mod(sum+=cnt);
        mod(ans+=cnt%P*Pow(m,lo)%P);
        //puts("\n");
    }else{
        for(int j=now;d+j<=n;j++){
            L[++tot]=j;
            dfs(d+j,j);
            tot--;
        }
    }
}
int main(){
    freopen("in","r",stdin);
    n=read();m=read();P=read();
    ini();
    dfs(0,1);
    //printf("%lld %lld\n",ans,sum);
    ans=ans*Pow(sum,P-2)%P;
    printf("%lld",ans);
}
时间: 2024-08-08 05:21:04

BZOJ 1815: [Shoi2006]color 有色图 [Polya DFS 重复合并]的相关文章

bzoj 1815: [Shoi2006]color 有色图 置换群

1815: [Shoi2006]color 有色图 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 136  Solved: 50[Submit][Status] Description Input 输入三个整数N,M,P 1< = N <= 53 1< = M < = 1000 N< P < = 10^ 9 Output 即总数模P后的余数 Sample Input input 1 3 2 97 Sample Output

poj 2154 Color(polya计数 + 欧拉函数优化)

http://poj.org/problem?id=2154 大致题意:由n个珠子,n种颜色,组成一个项链.要求不同的项链数目,旋转后一样的属于同一种,结果模p. n个珠子应该有n种旋转置换,每种置换的循环个数为gcd(i,n).如果直接枚举i,显然不行.但是我们可以缩小枚举的数目.改为枚举每个循环节的长度L,那么相应的循环节数是n/L.所以我们只需求出每个L有多少个i满足gcd(i,n)= n/L,就得到了循环节数为n/L的个数.重点就是求出这样的i的个数. 令cnt = gcd(i,n) =

hzau 1208 Color Circle(dfs)

1208: Color Circle Time Limit: 1 Sec  Memory Limit: 1280 MBSubmit: 289  Solved: 85[Submit][Status][Web Board] Description There are colorful flowers in the parterre in front of the door of college and form many beautiful patterns. Now, you want to fi

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

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

[BZOJ 1082] [SCOI2005] 栅栏 【二分 + DFS验证(有效剪枝)】

题目链接:BZOJ - 1082 题目分析 二分 + DFS验证. 二分到一个 mid ,验证能否选 mid 个根木棍,显然要选最小的 mid 根. 使用 DFS 验证,因为贪心地想一下,要尽量先用提供的小的木木棍,尽量先做出需要的大的木棍,所以要先将提供的木棍和需要的木棍都排序. DFS 的时候是按照需要的木棍从大到小的顺序一层一层搜,每一层上是按照从小到大的顺序枚举提供的木棍.(当然枚举的时候已经不一定是从小到大了,有些木棍已经被截掉了一些.) 要使用两个有效的剪枝: 1)如果下一层的木棍和

POJ2154 Color【Polya定理】【欧拉函数】【整数快速幂】

题目链接: http://poj.org/problem?id=2154 题目大意: 给定 N 种颜色的珠子,每种颜色珠子的个数均不限,将这些珠子做成长度为 N 的项链. 问能做成多少种不重复的项链,最后结果对 P 取模.并且两条项链相同,当且仅当两条 项链通过旋转后能重合在一起,且对应珠子的颜色相同. 解题思路: Polya定理的应用.先来看Polya定理. Polya定理:设 G = {a1,a2,-,ag}是 N 个对象的置换群,用 M 种颜色给这 N 个 对象着色,则不同的着色 方案数为

bzoj 2819 Nim(BIT,dfs序,LCA)

2819: Nim Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1596  Solved: 597[Submit][Status][Discuss] Description 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家. 为了设计漂亮一点的

[BZOJ 1004][HNOI2008]Cards(Polya定理/Burnside引理)

Description 小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有 多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方 案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案. 两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗 成另一种.Sun发现这个问题有点

BZOJ 2809: [Apio2012]dispatching [主席树 DFS序]

传送门 题意:查询树上根节点值*子树中权值和$\le m$的最大数量 最大值是多少 求$DFS$序,然后变成区间中和$\le m$最多有几个元素,建主席树,然后权值线段树上二分就行了 $WA$:又把边表开小了..... 好吧我$zz$了有根树加无向边干什么.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #de