题目
描述
一个\(n\)个珠子的手环,你可以选择其中\(m\)个将其染成金色;
但是连续的金色段的长度不能超过一个阈(yu,四声)值;
求:在旋转置换下的本质不同的方案数;
答案对\(998244353\)取模;
范围
$1 \le T \le 5 , 1 \le n \le 10^6 , 0 \le k \le m \le n $ ;
题解
和这个题类型差不多:
https://www.cnblogs.com/Paul-Guderian/p/10590523.html同样有两种推法:
设\(f(n,m)\)为不计旋转置换下的方案;
首先套用\(polya\)
\[ \begin{align} ans &= \frac{1}{n}\sum_{d=1}^{n} f(gcd(d,n),\frac{m}{\frac{n}{gcd(d,n)}})[\frac{n}{gcd(d,n)}|m]\\ &= \frac{1}{n}\sum_{d|(n,m)}f(\frac{n}{d},\frac{m}{d})\phi(d) \end{align} \]
给出两种推\(f(n,m)\)的方法;
\[ 考虑在先搞好剩下的n-m个无色珠子,然后将m个金珠子插进去,注意分两边和中间插\\ 容易得到答案对m的生成函数:F(x) = (\sum_{i=0}^{k}x^i)^{n-m-1}(\sum_{i=0}^{k}(i+1)x^i)\\ 令G(x)=\sum_{i=0}^{k}(i+1)x^i\\ xG(x) = \sum_{i=1}^{k+1}ix^i = G(x) - \sum_{i=0}^{k}x^i + x^{k+1}(k+1) \\ G(x) = \frac{1-x^{k+1}(k+1)+x^{k+2}(k+1)}{(1-x)^2}\\ 那么:\\ F(x) = \frac{(1-x^{k+1})^{n-m-1}}{(1-x)^{n-m+1}}[1-x^{k+1}(k+2)+x^{k+2}(k-1)]\\ 用二项式定理和广义二项式定理(或者直接说泰勒展开)展开左边;\\ 直接求的复杂度是\frac{\sigma(m)}{k+1}的\\\sigma(m)接近n loglog \ n ;\\ 所以复杂度接近线性; \]另外一种是直接从组合的意义去计算\(f(n,m)\);
\[ 直接考虑去染金m个连续不超过k的珠子比较麻烦;\\ 考虑成将n-m个珠子染黑,然后相邻两个珠子之间的距离不超过k\\ 下文的m均指原定义中的n-m\\ 考虑限定第一个珠子是黑色的,最后的放案需要乘以\frac{n}{m}\\ 相当于选取m个整数变量,满足:\\ 1.\ 0 \le a_0,a_1,...a_{m-1}\le k\\ 2.\ a_0+a_1+\cdots+a_{m-1} = n-m\\ 容斥1,插隔板统计2,可得最后的答案\\ \frac{n}{m}\sum_{i=1}^{k}(-1)^i(^m_i)(^{n-1-(k+1)i}_{m-1});\\ 类似复杂度分析\\ \]#include<bits/stdc++.h> #define ll long long #define mod 998244353 using namespace std; const int N=1000010; int n,m,k,iv[N],fac[N],inv[N],vis[N],pr[N],phi[N],pt; int pw(int x,int y){ int re=1; while(y){ if(y&1)re=(ll)re*x%mod; y>>=1;x=(ll)x*x%mod; } return re; } void pre(){ iv[1]=1;for(int i=2;i<=1000000;++i)iv[i]=(ll)(mod-mod/i)*iv[mod%i]%mod; for(int i=fac[0]=inv[0]=1;i<=1000000;++i){ fac[i]=(ll)fac[i-1]*i%mod; inv[i]=(ll)inv[i-1]*iv[i]%mod; } phi[1]=1; for(int i=2;i<=1000000;++i){ if(!vis[i])phi[pr[++pt]=i]=i-1; for(int j=1,t;j<=pt&&i*pr[j]<=1000000;++j){ vis[t=i*pr[j]]=1; if(i%pr[j]==0){phi[t]=phi[i]*pr[j];break;} else phi[t]=phi[i]*(pr[j]-1); } } } int C(int x,int y){return x<y?0:(ll)fac[x]*inv[y]%mod*inv[x-y]%mod;} int cal(int n,int m){ if(n<=m)return 0; m=n-m; int re=0; for(int i=0;i<=m&&i*k<n;++i){ int now=(ll)C(m,i)*C(n-1-i*k,m-1)%mod; if(i&1)re=(re-now+mod)%mod; else re=(re+now)%mod; } return (ll)re*n%mod*iv[m]%mod; } int main(){ freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); pre(); int T;scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&k);k++; if(!m){puts("1");continue;} int ans=0; for(int i=1;i<=n&&i<=m;++i)if(n%i==0&&m%i==0){ ans=(ans+(ll)cal(n/i,m/i)*phi[i]%mod)%mod; } ans=(ll)iv[n]*ans%mod; cout<<ans<<endl; } return 0; }