POJ2888 Magic Bracelet(Burnside引理+矩阵快速幂优化DP)

51 篇文章 1 订阅
POJ2888 Magic Bracelet原题地址:http://poj.org/problem?id=2888题意: T组数据。 有n个珠子串成一个环,珠子共有m种,有k个要求说第x种和第y种珠子不能相邻。 我们认为两个可以通过旋转得到的环是同一种,问一共能串出多少本质不同的环。对9973取模。数据范围 1 ≤ n ≤ 1e9, gcd(n, 9973) = 1,1 ≤ m
摘要由CSDN通过智能技术生成

POJ2888 Magic Bracelet

原题地址http://poj.org/problem?id=2888

题意:
T组数据。
有n个珠子串成一个环,珠子共有m种,有k个要求说第x种和第y种珠子不能相邻。
我们认为两个可以通过旋转得到的环是同一种,问一共能串出多少本质不同的环。对9973取模。

数据范围
1 ≤ n ≤ 1e9, gcd(n, 9973) = 1,1 ≤ m ≤ 10, 1 ≤ k ≤ m(m − 1) ⁄ 2

题解:
这篇题解讲的很清楚。

重新学习burnside引理:
L=1|G|nk=1|Zk|=1|G|si=1D(ai)
倘若没有限制的话,由此可以得到Polya定理:
L=1|G|(mc(g1)+mc(g2)++mc(gs))

对于此题,对于n个珠子的一个环,他的置换有 0,1,2…n-1共n个,
要求每个置换的 D(ai) ,置换 ai 下不变元素的个数。

观察发现,一个长为n的环的置换i,其循环节个数为 gcd(n,i)
例如:
对于一个长为6的环,其置换 {1,2,3,4,5,65,6,1,2,3,4}

这里写图片描述
因为1可以转到3,3可以转到5,2可以转到4,4可以转到6,
有两个循环{1,3,5}{2,4,6},每个循环的长度为3。 gcd(6,2)=2 。每两个分成一组每组就是每个循环中各取了一个元素。
如果要是在这个置换下不变,那么一个循环必须染同一种颜色。

于是求在置换i下不变元素的个数,就是求这个小长为 gcd(n,i) 的小块的染色方案,记为 f(gcd(n,i)) ,这个可以用矩阵快速幂优化DP求。
答案 =1nni=1f(gcd(n,i))
=1nd|nf(d)ϕ(n/d)

我觉着这个复杂度有点虚,但是因为d的个数最多不到2000,所以没没毛病。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=11;
const int mod=9973;
struct Matrix
{
    int n,a[N][N];
    void init() {for(int i=1;i<=10;i++) a[i][i]=1; n=10;}
}e,a;
Matrix operator*(const Matrix &A,const Matrix &B)
{
    Matrix C; C.n=A.n;
    memset(C.a,0,sizeof(C.a));
    for(int i=1;i<=A.n;i++) 
    for(int j=1;j<=A.n;j++)
    for(int k=1;k<=A.n;k++)
    C.a[i][j]+=A.a[i][k]*B.a[k][j];                         
    for(int i=1;i<=A.n;i++) 
    for(int j=1;j<=A.n;j++)
    C.a[i][j]%=mod;
    return C;
}
int modpow(int A,int B)
{
    int ret=1; int base=A;
    for(;B;B>>=1)
    {
        if(B&1) ret=(1LL*ret*base)%mod;
        base=(1LL*base*base)%mod;
    }
    return ret;
}
int T,n,m,k;
int phi(int x)
{
    int ret=x; 
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            ret=ret/i; ret=ret*(i-1);
            while(x%i==0) x=x/i;
        }
    }
    if(x>1) {ret=ret/x; ret=ret*(x-1);}
    return ret;
}
int cal(Matrix base,int x)
{
    Matrix ret=e; ret.n=m;  int ans=0;
    for(;x;x>>=1)
    {
        if(x&1) ret=ret*base;
        base=base*base;
    }
    for(int i=1;i<=m;i++) {ans=(ans+ret.a[i][i]); if(ans>=mod) ans-=mod;}
    return ans; 
}
int getans(int x)
{
    int ans=cal(a,x);
    ans=(1LL*ans*phi(n/x))%mod;
    return ans;
}
int main()
{   
    scanf("%d",&T);
    e.init(); int ret;
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        a.n=m; ret=0;
        for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) a.a[i][j]=1;
        for(int i=1;i<=k;i++)
        {
            int x,y; scanf("%d%d",&x,&y);
            a.a[x][y]=a.a[y][x]=0;
        }
        for(int i=1;i*i<=n;i++)
        if(n%i==0) {ret+=getans(i); if(n/i!=i) ret+=getans(n/i); if(ret>=mod) ret%=mod; }           
        ret=(1LL*ret*modpow(n,mod-2))%mod;
        printf("%d\n",ret);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Java解决POJ3233—矩阵幂序列问题的代码和解释: ```java import java.util.Scanner; public class Main { static int n, k, m; static int[][] A, E; public static void main(String[] args) { Scanner sc = new Scanner(System.in); n = sc.nextInt(); k = sc.nextInt(); m = sc.nextInt(); A = new int[n][n]; E = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { A[i][j] = sc.nextInt() % m; E[i][j] = (i == j) ? 1 : 0; } } int[][] res = matrixPow(A, k); int[][] ans = matrixAdd(res, E); printMatrix(ans); } // 矩阵乘法 public static int[][] matrixMul(int[][] a, int[][] b) { int[][] c = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % m; } } } return c; } // 矩阵快速幂 public static int[][] matrixPow(int[][] a, int b) { int[][] res = E; while (b > 0) { if ((b & 1) == 1) { res = matrixMul(res, a); } a = matrixMul(a, a); b >>= 1; } return res; } // 矩阵加法 public static int[][] matrixAdd(int[][] a, int[][] b) { int[][] c = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { c[i][j] = (a[i][j] + b[i][j]) % m; } } return c; } // 输出矩阵 public static void printMatrix(int[][] a) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { System.out.print(a[i][j] + " "); } System.out.println(); } } } ``` 解释: 1. 首先读入输入的n、k、m和矩阵A,同时初始化单位矩阵E。 2. 然后调用matrixPow函数求出A的k次幂矩阵res。 3. 最后将res和E相加得到结果ans,并输出。 4. matrixMul函数实现矩阵乘法,matrixPow函数实现矩阵快速幂,matrixAdd函数实现矩阵加法,printMatrix函数实现输出矩阵。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值