P4916 做题笔记
P4916 做题笔记
学习了 \(Burnside\) 引理和 \(P\acute{o}lya\) 定理(事实上以前就学过不过全忘了),做一道题来复习一下!
题意:给一个结点个数为 \(n\) 环进行黑白染色,然后定义一种染色方案合法当且仅当:其中有恰好 \(m\) 个染成黑色,并且最长连续黑串的长度不超过 \(k\) ,求本质不同的染色方案数。定义两种染色方案本质相同当且仅当可以将第一种染色方案染成的环通过旋转得到第二种染色方案染成的环。
旋转同构!染色!考虑到有这么多限制我们很难用 \(P\acute{o}lya\) 定理做,所以我们考虑 \(Burnside\) 引理。根据亓爷爷的教育:“由于染色集合通常是巨大的,所以我们考虑每种置换的稳定点。”我们发现在环上一个置换的稳定点通常只跟他的循环数量有关,所以我们定义 \(f(d)\) 表示有 \(d\) 个循环的置换的稳定点。于是答案就可以表示为:
\(\,\,\,\,\,\,\dfrac{\sum_{i=1}^{n}f(\gcd(i,n))}{n}\)
\(=\dfrac{\sum_{d|n}f(d)\sum_{i=1}^{n}[\gcd(i,n)=d]}{n}\)
\(=\dfrac{\sum_{d|n}f(d)\sum_{i=1}^{\left\lfloor\frac{n}{i}\right\rfloor}[\gcd(i,\dfrac{n}{d})=1]}{n}\)
\(=\dfrac{\sum_{d|n}f(d)\varphi(\dfrac{n}{d})}{n}\)
然后那个 \(\varphi\) 直接 \(\Theta(\sqrt{n})\) 求即可,问题变成 \(f(d)\) 怎么求。
我们可以这么理解,我们把整个环分成 \(\dfrac{n}{d}\) 个部分,其中每个部分的大小是 \(d\) ,并且每个部分的染色都完全相同(只有这样在进行 \(i\) 次置换之后才可以保持不变),所以首先一个条件是 \(\dfrac{n}{d}|m\) ,然后我们就可以把每个部分当成一个环,求这一个环的答案就好了。抽离模型,问题变成:长度为 \(n\) 的环,染 \(m\) 个色,最长黑串长度不超过 \(k\) 的染色方案数。注意这里与原问题的区别:我们不需要本质不同!!然后我们再转化:在 \(n-m\) 个白球构成的环中插入 \(m\) 个黑球,然后每个缝隙的黑球数量不超过 \(k\) 个。先破环为链,先枚举开头结尾有多少个黑球,设有 \(i\) 个,然后再枚举开头有多少个,总共有 \(i+1\) 种方案。写的时候我突然有个疑惑:无论开头有多少个回到原环后都是本质相同的!但事实上原环只是稳定子中的元素,稳定子不要求本质相同。然后再考虑中间 \(n-m-1\) 个缝隙放 \(m-i\) 个黑球,每个缝隙不超过 \(k\) 个。这就非常显然的容斥了,我们钦定至少有几个超过了 \(k\) ,先给这些地方放 \(k+1\) 个,然后变成了剩下的球随便放,然后乘以容斥系数即可。具体的,将 \(n\) 个球放进 \(m\) 个盒子,每个盒子的容量为 \(k\): \(R(n,m,k)=\dbinom{n+m-1}{m-1}+\sum_{i=1}^{i\le n,i(k+1)\le n}(-1)^{i}\dbinom{n}{i}\dbinom{n-i(k+1)+m-1}{m-1}\)
因为总共会有 \(\sigma(\gcd(n,m))\) 次求这个函数,每次求这个函数枚举 \(i\) 到 \(k\) ,对于每个 \(i\) 我们求那个 \(R\) 是 \(\dfrac{n}{k}\) ,所以时间复杂度是科学的。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 998244353
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int n,m,K;
ll ans;
//C
ll poww(ll a,int b)
{
ll ans=1,base=a;
while(b)
{
if(b&1) ans=ans*base%MOD;
base=base*base%MOD;
b>>=1;
}
return ans;
}
ll fac[MAXN*2],invfac[MAXN*2],inv[MAXN*2];
void Cinit()
{
fac[0]=invfac[0]=1,n=100000;
FUP(i,1,n*2) fac[i]=fac[i-1]*i%MOD;
invfac[n*2]=poww(fac[n*2],MOD-2),inv[n*2]=invfac[n*2]*fac[n*2-1]%MOD;
FDW(i,n*2-1,1) invfac[i]=invfac[i+1]*(i+1)%MOD,inv[i]=invfac[i]*fac[i-1]%MOD;
}
ll C(int x,int y)
{
if(x<0||y<0||x<y) return 0;
return fac[x]*invfac[y]%MOD*invfac[x-y]%MOD;
}
//phi
int phi(int x)
{
ll ans=x,tmp=x;
//printf("ans=%lld tmp-1=%lld inv[tmp]=%lld\n",ans,tmp-1,inv[tmp]);
for(int i=2;i*i<=n;i++)
{
if(!(tmp%i))
{
ans=ans*(i-1)%MOD*inv[i]%MOD;
while(!(tmp%i)) tmp/=i;
if(tmp==1) break;
}
}
if(tmp!=1) ans=ans*(tmp-1)%MOD*inv[tmp]%MOD;
return ans;
}
ll R(int x,int y)
{
if(!y&&!x) return 1;
ll re=C(x+y-1,y-1);
for(int i=1;i<=y&&i*(K+1)<=x;i++)
{
ll del=C(y,i)*C(x-i*(K+1)+y-1,y-1)%MOD;
//printf("i=%d del=%lld %d\n",i,del,C(x-i*(K+1)+y-1,y-1));
if(i&1) re=(re-del+MOD)%MOD;
else re=(re+del)%MOD;
}
//printf("R :x=%d y=%d re=%lld\n",x,y,re);
return re;
}
ll fun(int d)
{
ll re=0;
if(m%(n/d)) return 0;
int x=d,y=m/(n/d);
//printf("# %d %d\n",x,y);
//if(x-y==1) return min(K,y)+1;
FUP(i,0,min(K,y)) re=(re+(i+1)*R(y-i,x-y-1)%MOD)%MOD;
//printf("d=%d re=%lld\n",d,re);
return re;
}
int main(){
Cinit();
n=read(),m=read(),K=read();
if(n==m)
{
if(K==n) puts("1");
else puts("0");
return 0;
}
for(int i=1;i*i<=n;i++)
{
if(!(n%i))
{
ans=(ans+fun(i)*phi(n/i)%MOD)%MOD;
if(i*i!=n) ans=(ans+fun(n/i)*phi(i)%MOD)%MOD;
}
}
printf("%lld\n",ans*inv[n]%MOD);
return 0;
}