2019 ICPC Asia 南昌 Regional J. Summon(矩阵快速幂优化DP+polay定理)

题意:

长度为 n ( n < 1 e 5 ) n(n<1e5) n(n<1e5)的环,填四种颜色,有 m m m个要求 ( m < 256 ) (m<256) (m<256),每个要求限制一个长度为 4 4 4的序列 X X X X XXXX XXXX不许在环中出现,问有多少合法方案。(通过旋转可以变相同的算同一种方案)

解题思路:

前置知识点:polay定理
在粗略的学习polay定理之后,我只记住了对于没有限制的环
方 案 数 = 1 置 换 数 ∗ ∑ 颜 色 数 c ( g i ) 方案数=\frac{1}{置换数}*\sum 颜色数^{c(g_i)} =1c(gi)
其中 c ( g i ) c(g_i) c(gi)表示在置换 g i g_i gi下的不动点个数。
只记住这个公式的话,有了限制就懵逼了,其实是因为没理解这个公式为什么是这样。
根据Burnside引理,方案数= 1 ∣ G ∣ ∑ D ( g i ) \frac{1}{|G|}\sum D(g_i) G1D(gi),其中|G|为置换的数目, D ( g i ) D(g_i) D(gi)表示在置换 g i g_i gi下的不动点个数。什么是不动点?就是当前状态置换之后还是当前状态的方案。
那么对于一个置换,它会形成若干个循环。比如说:
长度为6的环,转2个,那么有两个循环:(1,3,5)和(2,4,6)。要使得置换前和置换后相同,每个环里的颜色必须相同。所以 颜色数^循环数=不动点数目。

那么回到这题。一共有 n n n个置换
那么答案= 1 n ∑ i = 1 n D ( g i ) \frac{1}{n}\sum_{i=1}^nD(g_i) n1i=1nD(gi),其中 D ( g i ) D(g_i) D(gi)为置换 g i g_i gi下不动点的个数。那么我们发现对于置换 g i g_i gi,只要确定了前 g c d ( i , n ) gcd(i,n) gcd(i,n)个颜色,那么后面的颜色就全部由前面的颜色完全确定了。
那么枚举 n n n的约数 d d d,易得 g c d ( i , n ) = d gcd(i,n)=d gcd(i,n)=d i i i ϕ ( n / d ) \phi(n/d) ϕ(n/d)个。
a ( i , j ) a(i,j) a(i,j)表示最后3个颜色状态是 i ( 4 ∗ 4 ∗ 4 ) i(4*4*4) i444到最后3个颜色状态是 j ( 4 ∗ 4 ∗ 4 ) j(4*4*4) j444是否可以转移。然后 ∑ a d ( i , i ) \sum a^d(i,i) ad(i,i)就是 g c d = d gcd=d gcd=d时候的不动点个数。

代码:

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int mod = 998244353;
const int N = 64;
const int maxn = 1e5 + 50;
int pri[maxn], phi[maxn], h[maxn], cnt = 0;
int qm(int a, int b){
    
    
    int res = 1;
    while(b){
    
    
        if(b&1) res = (ll) res*a%mod;
        a = (ll)a*a%mod;
        b >>= 1;
    }return res;
}
void INIT(){
    
    
    int k;
    phi[1] = 1;
    for(int i = 2; i < maxn; ++i){
    
    
        if(!h[i]) phi[pri[cnt++] = i] = i-1;
        for(int j = 0; j < cnt && (k=i*pri[j])<maxn; ++j){
    
    
            h[k] = 1;
            if(i%pri[j]) phi[k] = (pri[j]-1)*phi[i];
            else{
    
    phi[k]=phi[i]*pri[j]; break;}
        }
    }
}
struct mt{
    
    
    int a[N][N];
    mt (){
    
    memset(a, 0, sizeof a);}
    void E(){
    
    for(int i = 0; i < N; ++i) a[i][i] = 1;}
    void F(){
    
    for(int i = 0; i < N; ++i) for(int j = 0; j < N; ++j) a[i][j] = 1;}
    mt operator * (mt x){
    
    
        mt ans;
        for(int i = 0; i < N; ++i){
    
    
            for(int k = 0; k < N; ++k){
    
    
                if(!a[i][k]) continue;
                for(int j = 0; j < N; ++j){
    
    
                    ans.a[i][j]+=(ll)a[i][k]*x.a[k][j]%mod;
                    ans.a[i][j] %= mod;
                }
            }
        }return ans;
    }
}base, pbase[20];
int n, m, vis[256];
mt qm(int b){
    
    
    mt res; res.E();
    for(int i = 0; b; b>>=1, ++i){
    
    
        if(b&1) res = res*pbase[i];
    }return res;
}
void init(){
    
    
    for(int i = 0; i < 64; ++i){
    
    
        for(int k = 0; k < 4; ++k){
    
    
            int nxt = (i*4+k)%64;
            base.a[i][nxt] = 1;
        }
    }
    cin>>n>>m;
//    n = 83160; m = 0;极限数据
    while(m--){
    
    
        int x = 0, t; for(int i = 0; i < 4; ++i) scanf("%d", &t), x = x*4+t;
        vis[x] = 1;
        base.a[x/4][x%64] = 0;
    }
    pbase[0] = base;
    for(int i = 1; i < 20; ++i) pbase[i] = pbase[i-1]*pbase[i-1];
}
int fun(int a,int b, int c, int d){
    
    
    return a*64+b*16+c*4+d;
}
void sol(){
    
    
    int ans = 0;
    for(int i = 0; i < 4; ++i){
    
    
        int x = 0; for(int j = 0; j < 4; ++j) x = x*4+i;
        if(!vis[x]) ans++;
    }
    ans = ans * phi[n];//约数1的影响
    if(n%2 == 0){
    
    
        int t = 0;
        for(int i = 0; i < 4; ++i){
    
    
            for(int j = 0; j < 4; ++j){
    
    
                int x = fun(i,j,i,j);
                int y = fun(j,i,j,i);
                if(!vis[x] && !vis[y]) t++;
            }
        }
        t = (ll)t*phi[n/2];
        ans = (ans+t)%mod;
    }//约数2的影响
    for(int i = 3; i <= n; ++i){
    
    
        if(n%i) continue;
        mt A; A.E();
        A = A*qm(i);
        int t = 0;
        for(int j = 0; j < 64; ++j) t = (t+A.a[j][j])%mod;
        t = (ll)t*phi[n/i]%mod;
        ans = (ans+t)%mod;
    }
    ans = (ll)ans*qm(n,mod-2)%mod;
    cout<<ans<<endl;
}
int c[maxn];
int main()
{
    
    
    INIT();
    init();
    sol();
}

猜你喜欢

转载自blog.csdn.net/qq_43202683/article/details/104149238