[SHOI2006]color 有色图[群论、组合计数]
题意
用 \(m\) 种颜色,给 \(n\) 个点的无向完全图的 \(\frac{n(n-1)}{2}\) 条边染色,两种方案相同当且仅当一种方案交换一些点的编号后可以变成另一种方案。问有多少本质不同的染色方案。
\(n\le 53, m\le 1000, n<mod\le 10^9\) 且 \(mod\) 为质数。
分析
- 考虑 \(Polya\) 定理。
- 假设已经枚举了一个点置换(对应唯一一种边置换),能否快速求出对应边的置换的循环个数?
- 对于两个点的循环(设长度分别为 \(l_1,l_2\)),它们之间的边构成了 \(\frac{l_1l_2}{lcm(l_1,l_2)}=gcd(l_1,l_2)\) 个边的循环。
- 对于一个点循环内部的边:假设初始在序列上选定的两个点的位置相差了 \(x\) ,在右端点转回序列左端后差值会变成 \(n-x\) 。可以得到,当且仅当 \(2x=n\) 时,循环长度为 \(\frac{n}{2}\)(无向图) ,其余时刻为 \(n\) 。所以当 \(n\) 为偶数时的边循环个数还要加 1。由于总边数为 \(\frac{n(n-1)}{2}\) ,所以边循环个数总可以写成 \(\lfloor\frac{n}{2}\rfloor\) 的形式。
- 那么对于一种点置换,假设其所有循环满足 \(l_1\le l_2\le \cdots\le l_k\) ,这样的置换个数为 \(\frac{n!}{l_1l_2\cdots l_kS_1!S_2!\cdots S_{max}!}\) ,其中 \(S_i\) 表示长度为 \(i\) 的循环个数。为什么是 \(l_i\) 而不是 \(l_i!\) 的原因是每次我们找 \(l_i\) 个位置作为一个循环之后,他们都连了一条指向其他位置的边,构成了一个环。环排列的个数是 \(\frac{n!}{n}=(n-1)!\) 。
- 综上,对于一种点置换,对应边置换的循环个数为 \(\sum_{i=1}^k\lfloor\frac{l_i}{2}\rfloor+\sum_{i=1}^{k-1}\sum_{j=i+1}^kgcd(l_i,l_j)\) 。
- 由于 \(n\le 53\) ,拆分的方案数在一个可接受的范围内。所以爆搜循环的拆分即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return x * f;
}
template <typename T> inline bool Max(T &a, T b){return a < b ? a = b, 1 : 0;}
template <typename T> inline bool Min(T &a, T b){return a > b ? a = b, 1 : 0;}
const int N = 60;
int n, m, mod;
LL fac[N], invfac[N], inv[N], ans;
int g[N][N], stk[N], num[N];
LL Pow(LL a, LL b) {
LL res = 1ll;
for(; b; b >>= 1, a = a * a % mod) if(b & 1) res = res * a % mod;
return res;
}
void add(LL &a, LL b) {
a += b;
if(a >= mod) a -= mod;
}
void solve( int tp) {
re(num);
rep(i, 1, tp) num[stk[i]]++;
LL cnt = fac[n];
rep(i, 1, tp) cnt = cnt * inv[stk[i]] % mod;
rep(i, 1, n) cnt = cnt * invfac[num[i]] % mod;
LL c = 0;
rep(i, 1, tp) c = (c + stk[i] / 2) % (mod - 1);
rep(i, 1, tp - 1)
rep(j, i + 1, tp) c = (c + g[stk[i]][stk[j]]) % (mod - 1);
add(ans, cnt * Pow(m, c) % mod);
}
void dfs(int dep, int rest, int lst) {
if(!rest) {
solve(dep - 1);
return;
}
for(int i = lst; i <= rest; ++i) stk[dep] = i, dfs(dep + 1, rest - i, i);
}
int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int main() {
n = gi(), m = gi(), mod = gi();
rep(i, 0, n)
rep(j, 0, n) g[i][j] = gcd(i, j);
inv[1] = fac[0] = invfac[0] = 1;
rep(i, 1, n) {
if(i ^ 1) inv[i] = (LL) (mod - mod / i) *inv[mod % i] % mod;
fac[i] = (LL) fac[i - 1] * i % mod;
invfac[i] = (LL) invfac[i - 1] * inv[i] % mod;
}
inv[0] = 1;
dfs(1, n, 1);
printf("%lld\n", ans * invfac[n] % mod);
return 0;
}
[SHOI2006]color 有色图[群论、组合计数]的更多相关文章
- bzoj 1815: [Shoi2006]color 有色图 置换群
1815: [Shoi2006]color 有色图 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 136 Solved: 50[Submit][Stat ...
- BZOJ1815: [Shoi2006]color 有色图
BZOJ1815: [Shoi2006]color 有色图 Description Input 输入三个整数N,M,P 1< = N <= 53 1< = M < = 1000 ...
- BZOJ 1815: [Shoi2006]color 有色图(Polya定理)
题意 如果一张无向完全图(完全图就是任意两个不同的顶点之间有且仅有一条边相连)的每条边都被染成了一种颜色,我们就称这种图为有色图. 如果两张有色图有相同数量的顶点,而且经过某种顶点编号的重排,能够使得 ...
- BZOJ 1815: [Shoi2006]color 有色图 [Polya DFS 重复合并]
传送门 题意: 染色图是无向完全图,且每条边可被染成k种颜色中的一种.两个染色图是同构的,当且仅当可以改变一个图的顶点的编号,使得两个染色图完全相同.问N个顶点,k种颜色,本质不同的染色图个数(模质数 ...
- bzoj 1478: Sgu282 Isomorphism && 1815: [Shoi2006]color 有色图【dfs+polya定理】
参考 https://wenku.baidu.com/view/fee9e9b9bceb19e8b8f6ba7a.html?from=search### 的最后一道例题 首先无向完全图是个若干点的置换 ...
- bzoj 2281 [Sdoi2011]黑白棋(博弈+组合计数)
黑白棋(game) [问题描述] 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色 ...
- BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
- BZOJ 4555: [Tjoi2016&Heoi2016]求和 [FFT 组合计数 容斥原理]
4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...
- 【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)
[HNOI2019]多边形(模拟,组合计数) 题面 洛谷 题解 突然特别想骂人,本来我考场现切了的,结果WA了几个点,刚刚拿代码一看有个地方忘记取模了. 首先发现终止态一定是所有点都向\(n\)连边( ...
随机推荐
- 标注的SQL拼接语句
方案一 exec sp_executesql N' SELECT T0.[WtmCode], T3.[opCode], T3.[opValue], T3.[CondId] FROM [dbo].[OW ...
- C#实现CRC校验
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Web; ...
- Echars鼠标点击事件多次触发
gChart.on('click', function (params) { if (params.componentSubType == "bar" && par ...
- Linux唤醒抢占----Linux进程的管理与调度(二十三)
1. 唤醒抢占 当在try_to_wake_up/wake_up_process和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当 ...
- c/c++ 网络编程与多线程 编译参数
网络编程与多线程 编译参数 编译时要链接操作系统的pthread库 g++ -g socket01.cpp -std=c++11 -pthread 不加-pthread的话,出现下面的错误: term ...
- monkey_recorder录制monkeyrunner脚本
转载:monkey_recorder录制monkeyrunner脚本 1. 你必须有android sdk, sdk的tools文件家里有一个monkeyrunner.bat.2. 将如下内容拷贝 ...
- HTTP与TCP的区别和联系
工作原理(转载): https://www.cnblogs.com/zimohul/p/6506406.html 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通 ...
- 详解PHP中的过滤器(Filter)
PHP 过滤器用于验证和过滤来自非安全来源的数据,比如用户的输入. 什么是 PHP 过滤器? PHP 过滤器用于验证和过滤来自非安全来源的数据. 验证和过滤用户输入或自定义数据是任何 Web 应用程序 ...
- shell 获取时间
获取当前时间 t=$(date +"%Y-%m-%d %H-%M-%S") echo $t 获取前一天的当前时间 time=$(date -d "-1 day" ...
- Welcom to Swift
1.第一个程序 import Foundation println(“hello world”) 2.常用数据类型 int/UInt/Double/Float/Bool/String/Array/Di ...