题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=6960
题目大意:一串手链有n个珠子,有三种颜色红蓝绿,求相邻颜色不同且绿色染色数不超过k的本质不同的染色方案数,这里说的本质不同为两种染色方案不能通过旋转相同
题目思路:根据Burnside原理
[|X/G|=frac{1}{|G|}sum_{gin G} |X^g|
]
设(f_{n,m})为给n个珠子染色,绿色不超过m的不考虑本质不同的染色方案数,根据Pólya原理,旋转i个会将置换分为gcd(i,n)段,每段长(frac{n}{gcd(i,n)}),这种情况下的不动点可看做(f_{gcd(i,n),j})
[ans
=frac{1}{n}sum_{i=1}^{n}sum_{j=0}^{lfloor frac{kcdotmathrm{gcd}(i,n)}{n}
floor} f_{gcd(i,n),j}\
=frac{1}{n}sum_{d|n}sum_{i=1}^{n}[mathrm{gcd}(i,n)=d]sum_{j=0}^{lfloor frac{kd}{n}
floor} f_{d,j}\
=frac{1}{n}sum_{d|n}sum_{i=1}^{lfloorfrac{n}{d}
floor}[mathrm{gcd}(i,n)=1]sum_{j=0}^{lfloor frac{kd}{n}
floor}f_{d,j}\
=frac{1}{n}sum_{d|n}varphi(lfloorfrac{n}{d}
floor)F(d,left lfloor frac{kd}{n}
ight
floor)
]
现在问题转化成如何求(f_{n,m}),首先假设我们放好了m个绿色后会形成m个空,每个空都有2种情况:红蓝红蓝红……,蓝红蓝红蓝……,共(2^{m})种情况,然后我们考虑怎么放绿色的,放绿色点的方案数等于在长度为n的手链上选取m个不相邻的点的方案数,也分两种情况,假设现在放了0个绿色的点,即在剩下的n-m个空中放m个绿色,方案数为(Cinom{m}{n-m}),假设现在放了1个绿色的点,其左右都不能放绿色,即在剩下的n-m-1个空中放m-1个绿色,方案数为(Cinom{m-1}{n-m-1}),故(f_{n,m}=2^{m}left (Cinom{m}{n-m}+Cinom{m-1}{n-m-1} ight ))
AC代码:
话说C++超时了但G++AC了
#include <unordered_map>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <stack>
#include <deque>
#include <queue>
#include <cmath>
#include <map>
#include <set>
using namespace std;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10, M = 4e7 + 10;
const int base = 1e9;
const int P = 131;
const int mod = 998244353;
const double eps = 1e-8;
const double PI = acos(-1.0);
ll fac[N], inv[N], pow2[N];
int phi[N], pri[N];
bool st[N];
int pos;
ll ksm(ll a, ll b)
{
ll res = 1 % mod;
while (b)
{
if (b & 1)
res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
void inif(int n)
{
phi[1] = 1;
for (int i = 2; i <= n; ++i)
{
if (!st[i])
{
phi[i] = i - 1;
pri[++pos] = i;
}
for (int j = 1; pri[j] <= n / i; ++j)
{
st[pri[j] * i] = true;
if (i % pri[j] == 0)
{
phi[pri[j] * i] = phi[i] * pri[j];
break;
}
phi[pri[j] * i] = phi[i] * (pri[j] - 1);
}
}
fac[0] = inv[0] = 1;
for (int i = 1; i < n; ++i)
{
fac[i] = (fac[i - 1] * i) % mod;
inv[i] = ksm(fac[i], mod - 2);
}
pow2[0] = 1;
for (int i = 1; i < n; ++i)
pow2[i] = pow2[i - 1] * 2 % mod;
}
ll C(ll n, ll m)
{
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
ll F(ll n, ll k)
{
ll res = n & 1 ? 0 : 2;
for (ll m = 1; m <= min(k, n / 2); ++m)
res = (res + pow2[m] * (C(n - m, m) + C(n - m - 1, m - 1)) % mod) % mod;
return res;
}
int main()
{
inif(N - 1);
int T;
scanf("%d", &T);
while (T--)
{
ll n, k;
scanf("%lld%lld", &n, &k);
ll ans = 0;
for (ll i = 1; i * i <= n; ++i)
{
if (n % i == 0)
{
ll d = i;
ans = (ans + phi[n / d] * F(d, k * d / n) % mod) % mod;
if (i != n / i)
{
d = n / i;
ans = (ans + phi[n / d] * F(d, k * d / n) % mod) % mod;
}
}
}
ans = ans * ksm(n, mod - 2) % mod;
printf("%lld
", ans);
}
return 0;
}