题意:
长度为 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不许在环中出现,问有多少合法方案。(通过旋转可以变相同的算同一种方案)
解题思路:
前置知识点:polya定理
在粗略的学习polya定理之后,我只记住了对于没有限制的环
方
案
数
=
1
置
换
数
∗
∑
颜
色
数
c
(
g
i
)
方案数=\frac{1}{置换数}*\sum 颜色数^{c(g_i)}
方案数=置换数1∗∑颜色数c(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)
∣G∣1∑D(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)
n1∑i=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)
i(4∗4∗4)到最后3个颜色状态是
j
(
4
∗
4
∗
4
)
j(4*4*4)
j(4∗4∗4)是否可以转移。然后
∑
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();
}