zoukankan      html  css  js  c++  java
  • [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}icdot 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_{0le ile 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}kcdot frac{frac{md}{n}}{k})=o(sigma_{1}(n))le o(nln 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
  • 相关阅读:
    二叉树
    基础1
    tcp/udp
    异步io模块(自定义)
    select+异步
    异步发送请求的几种方式
    多线程,进程实现并发请求
    位解包运算
    从前端程序员的视角看小程序的稳定性保障
    运行node 报错 throw er; // Unhandled 'error' event
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/13856324.html
Copyright © 2011-2022 走看看