3133. 串珠子

题目链接

3133. 串珠子

给定 M 种不同颜色的珠子,每种颜色的珠子的个数都足够多。

现在要从中挑选 N 个珠子,串成一个环形手链。

请问一共可以制作出多少种不同的手链。

注意,如果两个手链经旋转或翻转后能够完全重合在一起,对应位置的珠子颜色完全相同,则视为同一种手链。

输入格式

输入包含多组测试数据。

每组测试数据占一行,包含两个整数 M,N

最后一行包含 0 0 表示输入结束。

输出格式

每组数据输出一个占一行的整数表示结果。

数据范围

N,M>0,
N×M32

输入样例:

1 1 2 1 2 2 5 1 2 5 2 6 6 2 0 0

输出样例:

1 2 3 5 8 13 21

样例解释

M=2,N=5 时,一共可以制作出 8 种不同的手链,如下图。

image

解题思路

polya

burnside引理:设 AB 为有限集合, X 为一些从 AB 的映射组成的集合。
GA 上的置换群,且 X 中的映射在 G 中的置换作用下封闭。
X/G 表示 G 作用在 X 上产生的所有等价类的集合
(若 X 中的两个映射经过 G 中的置换作用后相等,则它们在同一等价类中),则

|X/G|=1|G|gG|Xg|

其中 |S| 表示集合 S 中元素的个数,且

Xg={xxX,g(x)=x}

burnside引理的直观理解:每种置换的不动点个数的平均值,即本质不同的方案数
polya定理:在与 Burnside 引理相同的前置条件下,若 X 为 所有从 AB 的映射,内容修改为

|X/G|=1|G|gG|B|c(g)

其中 c(g) 表示置换 g 能拆分成的不相交的循环置换的数量
polya定理即求每种置换的不动点个数,但是必须保证其拆分的循环置换不相交

本题有两种置换:旋转和翻转
对于旋转来说,方向没有影响,假设都顺时针旋转,对于每个点来说旋转可以每次旋转 0,1,2,,n1 次,每次旋转 k,设旋转 t 次后 i 回到原位置,即 i+k×ti(modn),解得 t=n/gcd(k,n),即每个轨迹的点数,对于 i+1 这个点来说每次旋转 k 次,跟 i 这个点的轨迹不会相交,且轨迹上的点数和 i 相等,即所有不相交的轨迹点数相等,总共有 n/t=gcd(k,n) 条不相交的轨迹
对于翻转来说,如果 n 为奇数,讨论其对称轴,共有 n 个对称轴,即 n 个置换群,考虑经过轴上的那个点,每个置换群共有 n+12 个循环置换;如果 n 为偶数,对称轴细分为是否经过点,对于经过点的对称轴,其共有 n2 个置换群,每个置换群有 2+n22=n2+1 个循环置换,对于不经过点的对称轴,其也有 n2 个置换群。每个置换群有 n2 个循环置换
故总的方案数为

{i=0n1mgcd(i,n)+n×mn+12n+nni=0n1mgcd(i,n)+n2×mn2+1+n2×mn2n+n2+n2n

  • 时间复杂度:O(logn)

代码

// Problem: 串珠子 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/3136/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } int m,n; int ksm(int a,int b) { int res=1; while(b) { if(b&1)res*=a; a*=a; b>>=1; } return res; } int main() { while(cin>>m>>n,m||n) { int res=0; for(int i=0;i<n;i++)res+=ksm(m,__gcd(i,n)); if(n&1) res+=n*ksm(m,n+1>>1); else res+=n/2*ksm(m,n/2+1)+n/2*ksm(m,n/2); cout<<res/(2*n)<<'\n'; } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16798809.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示