[loj6519]魔力环

根据Burnside引理,枚举旋转的步数$k$,求不动点的数目

令$d=\gcd(n,k)$,这个问题其实可以等价于填$\frac{n}{d}$个长度为$d$的环(因此很明显$\frac{n}{d}|m$是必要条件)

对于$d$相同的$k$是等价的,因此不妨枚举$d$,则$k$的数量即为$\sum_{i=1}^{\frac{n}{d}}[\gcd(i,\frac{n}{d})=1]=\varphi(\frac{n}{d})$

记$n'=d,m'=\frac{md}{n}$,对于这个子问题,由于其旋转所得是不同的,因此枚举首尾的黑点数量之和,即$ans=\sum_{i=0}^{k}i\cdot f(n'-i-2,m'-i)$,其中$f(n,m)$表示这个子问题在序列上的答案

(这里要特判,当$m'\le k$时答案显然为$c(n',m')$)

考虑$f(n,m)$的计算,可以看作将$m$个黑点分为至多$n-m+1$份(可以为空且不超过$k$)的方案数

令$n'=n-m+1$,容斥存在$i$份超过$k$,将他们都减去$k$,那么这$n'$份就没有限制,再根据插板法即可得到$f(n,m)=\sum_{0\le i\le n',i(k+1)\le m}(-1)^{i}\cdot c(n',i)\cdot c(m-i(k+1)+n'-1,n'-1)$

时间复杂度:$o(\sum_{d|n}k\cdot \frac{\frac{md}{n}}{k})=o(\sigma_{1}(n))\le o(n\ln n)$(先枚举$d$,再枚举$i$,最后计算$f$),可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 #define mod 998244353
 5 int n,m,k,ans,fac[N],inv[N],p[N],vis[N],phi[N];
 6 int c(int n,int m){
 7     return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod;
 8 } 
 9 int f(int n,int m){
10     int nn=n-m+1,ans=0;
11     for(int i=0;(i<=nn)&&(i*(k+1)<=m);i++){
12         int s=1LL*c(nn,i)*c(m-i*(k+1)+nn-1,nn-1)%mod;
13         if (i&1)ans=(ans+mod-s)%mod;
14         else ans=(ans+s)%mod;
15     }
16     return ans;
17 }
18 int calc(int n,int m){
19     if (m<=k)return c(n,m);
20     int ans=0;
21     for(int i=0;i<=k;i++)
22         if ((n-i-2>=0)&&(m>=i))ans=(ans+1LL*(i+1)*f(n-i-2,m-i))%mod;
23     return ans;
24 }
25 int main(){
26     scanf("%d%d%d",&n,&m,&k);
27     fac[0]=inv[0]=inv[1]=phi[1]=1;
28     for(int i=1;i<N-4;i++)fac[i]=1LL*fac[i-1]*i%mod;
29     for(int i=2;i<N-4;i++)inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
30     for(int i=1;i<N-4;i++)inv[i]=1LL*inv[i-1]*inv[i]%mod;
31     for(int i=2;i<N-4;i++){
32         if (!vis[i]){
33             p[++p[0]]=i;
34             phi[i]=i-1;
35         }
36         for(int j=1;(j<=p[0])&&(i*p[j]<N-4);j++){
37             vis[i*p[j]]=1;
38             if (i%p[j])phi[i*p[j]]=phi[i]*phi[p[j]];
39             else{
40                 phi[i*p[j]]=phi[i]*p[j];
41                 break;
42             }
43         }
44     } 
45     for(int i=1;i*i<=n;i++){
46         if ((n%i==0)&&(m%(n/i)==0))ans=(ans+1LL*phi[n/i]*calc(i,m/(n/i)))%mod;
47         if ((i*i!=n)&&(n%i==0)&&(m%i==0))ans=(ans+1LL*phi[i]*calc(n/i,m/i))%mod;
48     }
49     printf("%lld",1LL*ans*fac[n-1]%mod*inv[n]%mod);
50 }
View Code

 

posted @ 2020-10-22 09:39  PYWBKTDA  阅读(147)  评论(0编辑  收藏  举报