Description
你有一个长度为n的有标号环,你需要将其中m个位置染色,要求不能出现长度大于k的连续被染色的段。
求本质不同的环的个数。
两个环本质不同,当且仅当它们不能通过旋转得到另一个。
k ≤ m ≤ n ≤ 1 0 6 k\leq m\leq n\leq 10^6 k≤m≤n≤106
Solution
看到不能循环同构,自然想到用Burnside引理求解
枚举每一种置换,向后转i步,计算不动点个数
根据简单数论知识(套路)可以得出,向后转i步以后整个环分成了 ( n , i ) (n,i) (n,i)组,每一组有 n ( n , i ) n\over (n,i) (n,i)n个元素,组中的染或不染的状态相同。
第p个元素属于第 ( p − 1 ) m o d ( n , i ) + 1 (p-1)\ mod \ (n,i)+1 (p−1) mod (n,i)+1组,也就是说原来的环被分成了 n ( n , i ) n\over (n,i) (n,i)n个子段,每个子段中的元素两两不在同一组。
我们发现每一个子段其实也可以看做一个环(因为它与两边与它完全一样的子段相接)。
并且由于有m的限制,我们还需要满足 ( n , i ) ∣ m (n,i)|m (n,i)∣m
我们只需要对所有n的约数求解即可。
现在只需要计算如何计算长度为n的带标号环,染m个元素,最长连续染色段不超过k。
一个环不太好搞,我们钦定1号元素是不染的,剩下的就相当于一条链了。
我们可以将链旋转1~n次,可以发现每个不染的元素都有可能是变成1号钦定的元素,因此每种环都被算了恰好n-m次,因此只需要将链的情况乘上
n
n
−
m
n\over n-m
n−mn即可。
考虑如何计算一条链的情况。
我们对于每一个不染的元素i,记一个变量
x
i
x_i
xi表示它与上一个不染的元素之间夹了多少个染了的元素,特别的
x
1
x_1
x1为链末尾的染色端长。
方案数相当于方程组
∑
i
=
1
n
−
m
x
i
=
m
\sum\limits_{i=1}^{n-m}x_i=m
i=1∑n−mxi=m
0
≤
x
i
≤
k
0\leq x_i \leq k
0≤xi≤k
的解的组数。
接下来就是套路了,采用容斥,枚举多少个变量必须超出上界限制,就给这些变量加上k+1,然后随便选。
那么 A n s = ∑ i = 0 n − m ( − 1 ) i ( n − m i ) ( m − i ( k + 1 ) + n − m − 1 n − m − 1 ) Ans=\sum\limits_{i=0}^{n-m}(-1)^i{n-m\choose i}{m-i(k+1)+n-m-1\choose n-m-1} Ans=i=0∑n−m(−1)i(in−m)(n−m−1m−i(k+1)+n−m−1)
这样计算一个是线性的,因此总的时间复杂度就是 O ( σ ( n ) ) O(\sigma(n)) O(σ(n))的,其中 σ ( n ) \sigma(n) σ(n)为n的约数和。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 1000005
#define mo 998244353
#define LL long long
using namespace std;
int t,n,m,l;
LL ct[N],js[N],ny[N];
bool bz[N];
int gcd(int x,int y)
{
return (y)?gcd(y,x%y):x;
}
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
LL C(int n,int m)
{
return ((n<m)?0:js[n]*ny[m]%mo*ny[n-m]%mo);
}
LL calc(int n,int m)
{
if(m==0) return 1;
if(m==n-1)
{
if(l>=n-1) return n;
else return 0;
}
LL s=0,v=1;
fo(i,0,n-m)
{
if(n-1-i*(l+1)<n-m-1) break;
s=(s+v*C(n-m,i)*C(n-1-i*(l+1),n-m-1)%mo+mo)%mo;
v=-v;
}
s=s*(LL)n%mo*ny[n-m]%mo*js[n-m-1]%mo;
return s;
}
int main()
{
cin>>t;
js[0]=1;
fo(i,1,N-5) js[i]=js[i-1]*(LL)i%mo;
ny[N-5]=ksm(js[N-5],mo-2);
fod(i,N-6,0) ny[i]=ny[i+1]*(LL)(i+1)%mo;
while(t--)
{
cin>>n>>m>>l;
memset(ct,0,sizeof(ct));
memset(bz,0,sizeof(bz));
LL s=0;
fo(i,1,n)
{
int c=gcd(n,i),w=n/c;
if(m%w==0)
{
int p=m/w;
if(p==c) s+=(l>=n);
else s=(s+((bz[c])?ct[c]:ct[c]=calc(c,p)))%mo;
bz[c]=1;
}
}
printf("%lld\n",s*ksm(n,mo-2)%mo);
}
}