BZOJ 1815: [Shoi2006]color 有色图(Polya定理)

题意

如果一张无向完全图(完全图就是任意两个不同的顶点之间有且仅有一条边相连)的每条边都被染成了一种颜色,我们就称这种图为有色图。

如果两张有色图有相同数量的顶点,而且经过某种顶点编号的重排,能够使得两张图对应的边的颜色是一样的,我们就称这两张有色图是同构的。

对于计算所有顶点数为 n ,颜色种类不超过 m 的图,最多有几张是两两不同构的图。

数据范围

n53,1m1000

题解

神仙题qwq

我们考虑对于点置换与其对应的边置换的关系:

  1. 对于不处在循环中的点对:

    假设第一个循环长度为 l1 第二个循环长度为 l2 ,那么循环节长度就是 lcm(l1,l2)

    一共有 l1×l2 对点对,每个点对所处的循环节长度都是一样的,那么循环节个数就是 l1l2lcm(l1,l2)=gcd(l1,l2)

  2. 对于处在循环中的点对:

    设循环长度为 l ,分奇偶讨论。

    • l 为奇数,那么循环长度刚好是 l ,一共有 (l2) 对点对,那么刚好就有 l12 个循环节。
    • l 为偶数,上面那种情况之外,还有转 l2 长度对应的循环节,那么一共有 (l2)l2l+1=l2 个循环节。

设一开始点置换划分的周期为 l1l2lk ,其中满足 i=1kli=n

那么循环节的个数其实就是:

i=1kli2+i=1kj=i+1kgcd(li,lj)

我们显然可以枚举所有 l 的集合,不难发现这就是整数划分,53 的划分数并不大。。。

只剩下最后一个问题,就是有多少个长为 n 的排列对应到 l1lk 这个点置换循环。

首先考虑可重排列计数,把 n 个数分给这些的方案为 n!i=1kli! 然后对于每个置换 i 内部是个圆排列,顺序有 (li1)! 。然后循环的先后顺序是互不影响的,要除掉 ci!(ci=j=1n[lj=i]) 个。

也就是

n!i=1klii=1kci!

总结

对于 Polya 定理,常常要找循环节个数。对于特殊的置换,我们常常可以利用循环长度是一样的性质,然后用总元素 / 循环长度,得到循环节个数。

代码

#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("1815.in", "r", stdin); freopen ("1815.out", "w", stdout); #endif } const int N = 65; int n, m, Mod; int cir[N], len, ans = 0; inline int fpm(int x, int power) { int res = 1; for (; power; power >>= 1, x = 1ll * x * x % Mod) if (power & 1) res = 1ll * res * x % Mod; return res; } void Dfs(int cur, int rest, int res) { if (!rest) { int coef = 1, cnt = 1; For (i, 1, len) { if (cir[i] != cir[i - 1]) cnt = 1; else ++ cnt; coef = 1ll * coef * cnt % Mod * cir[i] % Mod; } ans = (ans + 1ll * fpm(m, res) * fpm(coef, Mod - 2)) % Mod; return; } For (i, cur, rest) { int tmp = i / 2; For (j, 1, len) tmp += __gcd(i, cir[j]); cir[++ len] = i; Dfs(i, rest - i, res + tmp); -- len; } } int main () { File(); n = read(); m = read(); Mod = read(); Dfs(1, n, 0); printf ("%d\n", ans); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10632209.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(570)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示