3134. 魔法手链

题目链接

3134. 魔法手链

给定 \(m\) 种不同颜色的魔法珠子,每种颜色的珠子的个数都足够多。

现在要从中挑选 \(n\) 个珠子,串成一个环形魔法手链。

魔法珠子之间存在 \(k\) 对排斥关系,互相排斥的两种颜色的珠子不能相邻,否则会发生爆炸。(同一种颜色的珠子之间也可能存在排斥

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

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

答案对 \(9973\) 取模。

输入格式

第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。

每组数据第一行包含三个整数 \(n,m,k\)

接下来 \(k\) 行,每行包含两个整数 \(a,b\),表示颜色 \(a\) 的珠子不能和颜色 \(b\) 的珠子相邻。

\(m\) 种颜色编号为 \(1 \sim m\)

输出格式

每组数据输出一行一个整数,表示答案。

数据范围

\(1 \le T \le 10\),
\(1 \le n \le 10^9\),
\(gcd(n, 9973) = 1\),
\(1 \le m \le 10\),
\(0 \le k \le \frac{m(m+1)}{2}\),
\(1 \le a,b \le m\)

输入样例:

4
3 2 0
3 2 1
1 2
3 2 2
1 1
1 2
3 2 3
1 1
1 2
2 2

输出样例:

4
2
1
0

解题思路

burnside

本题即 3133. 串珠子 的加强版,由于多了循环置换之间的限制,所以不能用 polya 定理,只能用 burnside 引理

本题只考虑旋转,每次旋转 \(k\) 次,旋转的次数为 \(n/gcd(k,n)\),共 \(gcd(k,n)\) 个可能相交的循环置换,则每次置换时只用考虑长度为 \(gcd(k,n)\) 这段形成环的方案数,先考虑对于非环的情况,\(dp\) 求解,\(f[i][j]\) 表示前 \(i\) 个点最后一个点为 \(j\) 时的方案数,这部分答案为前一个合法状态的总和,但 \(n\) 过大,考虑矩阵优化,设 \(F[i]=f[i][j]\),可以发现:\(F[i+1]=F[i]\times A\),其中 \(A\) 为限制矩阵,则有 \(F^n=F_1\times A^n\),故可用矩阵乘法优化 \(dp\),另外这里不需要考虑环的情况,因为矩阵乘法已经满足首尾限制了,还有一点,由于 \(n\) 过大,不可能枚举所有的 \(k\),但是可以找出有多少个这样的 \(gcd(k,n)\),设 \(d=gcd(k,n)\),则这样的 \(d\)\(\phi{(n/d)}\)

\(int\) 范围内的最多约数个数为 \(1600\),故:

  • 时间复杂度:\(O(T\times 1600\times (m^3\times logn+\sqrt{n})\)

代码

// Problem: 魔法手链
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/3137/
// Memory Limit: 64 MB
// Time Limit: 3000 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;
}

const int N=15,mod=9973;
int t,n,m,k;
struct matrix
{
	int a[N][N];
	matrix()
	{
		memset(a,0,sizeof a);
	}
};
matrix operator*(matrix a,matrix b)
{
	matrix c;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=m;k++)
				c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
	return c;
}
int phi(int n)
{
	int res=n;
	for(int i=2;i<=n/i;i++)
		if(n%i==0)
		{
			res=res/i*(i-1);
			while(n%i==0)n/=i;
		}
	if(n>1)res=res/n*(n-1);
	return res%mod;
}
int ksm(matrix a,int n)
{
	matrix res;
	for(int i=1;i<=m;i++)res.a[i][i]=1;
	while(n)
	{
		if(n&1)res=res*a;
		a=a*a;
		n>>=1;
	}
	int ret=0;
	for(int i=1;i<=m;i++)ret=(ret+res.a[i][i])%mod;
	return ret;
}
int qmi(int a,int b,int p)
{
	int res=1%p;
	while(b)
	{
		if(b&1)res=1ll*res*a%p;
		a=1ll*a*a%p;
		b>>=1;
	}
	return res;
}
int main()
{
	
    for(scanf("%d",&t);t;t--)
    {
    	scanf("%d%d%d",&n,&m,&k);
    	matrix ma;
    	for(int i=1;i<=m;i++)
    		for(int j=1;j<=m;j++)ma.a[i][j]=1;
    	for(int i=1;i<=k;i++)
    	{
    		int a,b;
    		scanf("%d%d",&a,&b);
    		ma.a[a][b]=ma.a[b][a]=0;
    	}
    	int res=0;
    	for(int i=1;i<=n/i;i++)
    		if(n%i==0)
    		{
    			res=(res+1ll*ksm(ma,i)*phi(n/i)%mod)%mod;
    			if(i!=n/i)
    				res=(res+1ll*ksm(ma,n/i)*phi(i)%mod)%mod;
    		}
    	printf("%d\n",res*qmi(n,mod-2,mod)%mod);
    }
    return 0;
}
posted @ 2022-10-17 22:03  zyy2001  阅读(77)  评论(0编辑  收藏  举报