poj2888 Magic Bracelet

哈利波特为了给女友金妮制作独特的魔法手链作为生日礼物,需要解决一个组合数学问题:如何在考虑到特定珠子类型不能相邻且忽略旋转重复的情况下,计算出不同手链的数量。此问题可通过Burnside引理和矩阵快速幂来解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/archives/3409
Description

Ginny’s birthday is coming soon. Harry Potter is preparing a birthday present for his new girlfriend. The present is a magic bracelet which consists of n magic beads. The are m kinds of different magic beads. Each kind of beads has its unique characteristic. Stringing many beads together a beautiful circular magic bracelet will be made. As Harry Potter’s friend Hermione has pointed out, beads of certain pairs of kinds will interact with each other and explode, Harry Potter must be very careful to make sure that beads of these pairs are not stringed next to each other.

There infinite beads of each kind. How many different bracelets can Harry make if repetitions produced by rotation around the center of the bracelet are neglected? Find the answer taken modulo 9973.

Input

The first line of the input contains the number of test cases.

Each test cases starts with a line containing three integers n (1 ≤ n ≤ 109, gcd(n, 9973) = 1), m (1 ≤ m ≤ 10), k (1 ≤ k ≤ m(m − 1) ⁄ 2). The next k lines each contain two integers a and b (1 ≤ a, b ≤ m), indicating beads of kind a cannot be stringed to beads of kind b.

Output

Output the answer of each test case on a separate line.

Sample Input

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
Sample Output

4
2
1
0
Source

POJ Monthly–2006.07.30, cuiaoxiang

考虑如果旋转i个单位 那么会有gcd(i,n)个长度为n/gcd(i,n)的循环节

那么因为burnside引理我们将原问题转化为给这gcd(i,n)个区间染色的方案数

证明可以首先考虑如果要完整的置换我需要lcm(i,n) 那么lcm(i,n)/i就是置换所需要的次数

n/(lcm(i,n))/i就是置换的个数了

那么因为只和前一个有关 所以两维的dp即可 又因为n太大所以考虑矩阵优化即可

那么我们考虑sqrt时间枚举区间的个数那么显然区间个数这么多的区间有多少种呢 就是n/i种 即n/i种置换 所以像前面的套路做一下即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int mod=9973;
const int N=1e5+10;
inline int ksm(ll b,int t){static ll tmp;
    for (tmp=1;t;b=b*b%mod,t>>=1) if (t&1) tmp=tmp*b%mod;return tmp;
}
struct matrix{
    int f[11][11];
}ts;bool not_prime[N];
int T,prime[N],phi[N],tot,n,m,k;
inline void inc(int &x,int v){x=x+v>=mod?x+v-mod:x+v;}
inline matrix multiply(const matrix &a,const matrix &b){
    matrix c;memset(c.f,0,sizeof(c.f));
    for (int i=1;i<=m;++i)
        for (int k=1;k<=m;++k)
            for (int j=1;j<=m;++j)
                inc(c.f[i][j],a.f[i][k]*b.f[k][j]%mod);
    return c;
}
inline void init(){phi[1]=1;
    for (int i=2;i<=1e5;++i){
        if (!not_prime[i]) prime[++tot]=i,phi[i]=i-1;
        for (int j=1;prime[j]*i<=1e5;++j){
            not_prime[prime[j]*i]=1;
            if (i%prime[j]==0){
                phi[prime[j]*i]=phi[i]*prime[j];break;
            }else phi[prime[j]*i]=phi[i]*phi[prime[j]];
        }
    }
}
inline int calc(int x){
    if (x<=1e5) return phi[x]%mod;
    int now=x;
    for (int j=1;prime[j]*prime[j]<=x&&j<=tot;++j){
        if (x%prime[j]) continue;
        now-=now/prime[j];while(x%prime[j]==0) x/=prime[j];
    }if (x>1) now-=now/x;return now%mod;
}
inline int gao(int t){
    static matrix b,c;b=ts;int tmp=0;memset(c.f,0,sizeof(c.f));
    for (int i=1;i<=m;++i) c.f[i][i]=1;
    for (;t;t>>=1,b=multiply(b,b)) if (t&1) c=multiply(c,b);
    for (int i=1;i<=m;++i) inc(tmp,c.f[i][i]);return tmp;
}
int main(){
    freopen("poj2888.in","r",stdin);
    T=read();init();
    while(T--){
        n=read();m=read();k=read();
        for (int i=1;i<=m;++i)
            for (int j=1;j<=m;++j) ts.f[i][j]=1;
        for (int i=1;i<=k;++i){
            int x=read(),y=read();
            ts.f[x][y]=ts.f[y][x]=0;
        }int ans=0;
        for (int i=1;i*i<=n;++i){
            if(n%i) continue;
            inc(ans,(ll)calc(i)*gao(n/i)%mod);
            if(i*i==n) continue;
            inc(ans,(ll)calc(n/i)*gao(i)%mod);
        }ans=ans*ksm(n,mod-2)%mod;printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值