[bzoj1547]周末晚会
前言
這是一道Burnside引理的應用題
題目相關
鏈接
題目大意
一個長度為nnn的010101圈,沒有超過kkk個的連續的111,求方案數
數據范圍
T≤50,n≤2000,k≤2000T\le50,n\le2000,k\le2000T≤50,n≤2000,k≤2000
題解
我們設fif_ifi?表示不含超過kkk個連續111的長度為iii的鏈的數量
我們考慮求fif_ifi?,用所有方案減去不合法方案數,對于一個長度為i?1i-1i?1的不合法方案,其后面再加上一個數字,其依然不合法,然后我們考慮因為加入最后一位而不合法的串,一定是最后k+1k+1k+1位是111,倒數第k+2k+2k+2位是000,并且其它位合法,那么直接遞推即可
我們設hih_ihi?表示長度為iii、包含至少一個000、不含超過連續kkk個111的環的數量(固定111號點)
先不說要如何計算
我們考慮答案,我們列出Burnside引理的式子
l=1∣G∣∑ai∈Gc1(ai)l=\frac{1}{|G|}\sum_{a_i\in G}c_1(a_i)l=∣G∣1?ai?∈G∑?c1?(ai?)
我們發現對于所有置換(即所有0≤i<n0\le i<n0≤i<n,轉動iii次),其c(ai)c(a_i)c(ai?)剛好是最小整除周期是iii的因數的方案數,實際操作的時候我們發現c(ai)=hgcd(i,n)c(a_i)=h_{gcd(i,n)}c(ai?)=hgcd(i,n)?
我們考慮如何求hih_ihi?,我們發現我們可以在一條鏈的兩端連上01???1001···1001???10形式的一串(其中111的數量要小于等于kkk),我們枚舉111的數量至少為幾,我們再枚舉接入的位置,我們發現,插入的元素越多,方案越多
我們設原串為XXX,插入的iii個111在頭與尾的串分別為AAA和BBB,我們發現,最后一定是“A0X0BA0X0BA0X0B”的形式,我們發現若111的個數為iii,那么就有i+1i+1i+1種方案
那么我們考慮對fff進行前綴和,并且對于所有iii直接轉移選iii~kkk個的方案,那么iii個的方案就會被轉移到i+1i+1i+1次,即可
我們發現只有iii是nnn的因數時,hih_ihi?是要求的,直接dpdpdp一個hih_ihi?在i≤ki\le ki≤k的時候是O(1)\mathcal O(1)O(1)的,i>ki>ki>k的時候是O(k)\mathcal O(k)O(k)的,那么總復雜度為
O(∑i>k,i∣nk)\mathcal O\left(\sum_{i>k,i|n}k\right)O???i>k,i∣n∑?k???
我們設大于kkk且是nnn的因數的數有xxx個,容易發現nnn的因數從大到小排,第jjj個的值小于等于nj\frac njjn?,那么k≤nxk\le\frac nxk≤xn?,那么x?k≤nx*k\le nx?k≤n,即直接dpdpdp是O(n)\mathcal O(n)O(n)的
另外,我們發現對于所有c(ai)c(a_i)c(ai?)的求和可以枚舉j=gcd(i,n),那么貢獻次數就是φ(ni)\varphi(\frac ni)φ(in?),我們可以通過線性篩篩φ\varphiφ,并且枚舉jjj,這樣就可以做到O(n)\mathcal O(n)O(n)的復雜度
綜上,總復雜度O(n)\mathcal O(n)O(n)
代碼
#include<bits/stdc++.h> typedef long long ll; #define rg register template <typename T> inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;} template <typename T> inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');} template <typename T> inline void print(const T x){if(x<0)putchar('-'),printe(-x);else printe(x);} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} const int maxn=2001; const ll mod=100000007; inline int pow(int x,int y) {int res=1;for(;y;y>>=1,x=(ll)x*x%mod)if(y&1)res=(ll)res*x%mod;return res; } inline void md(int&x){if(x>=mod)x-=mod;} inline int Md(const int x){return x>=mod?x-mod:x;} int n,k,bit[maxn],F[maxn+5],*f=F+5,h[maxn],sum; ll T,ans; bool isprime[maxn];int prime[maxn],primesize,phi[maxn]; inline void getphi(const int size) {phi[1]=1;memset(isprime,1,sizeof(isprime));for(rg int i=2;i<=size;i++){if(isprime[i])prime[++primesize]=i,phi[i]=i-1;for(rg int j=1;j<=primesize&&(ll)prime[j]*i<=size;j++){isprime[prime[j]*i]=0;if(i%prime[j]==0){phi[prime[j]*i]=phi[i]*prime[j];break;}phi[prime[j]*i]=phi[i]*(prime[j]-1);}} } int main() { // freopen("party.in","r",stdin),freopen("party.out","w",stdout);bit[0]=1;for(rg int i=1;i<=2000;i++)bit[i]=Md(bit[i-1]<<1);getphi(2000);read(T);while(T--){read(n),read(k);if(k>n)k=n;sum=0,ans=0;memset(F,0,sizeof(F));memset(h,0,sizeof(h));for(rg int i=0;i<=k;i++)f[i]=bit[i];f[-1]=1;for(rg int i=k+1;i<=n;i++){sum=Md(Md(sum<<1)+f[i-k-2]);f[i]=Md(bit[i]-sum+mod);}for(rg int i=0;i<=n;i++)f[i]=Md(f[i]+f[i-1]);for(rg int i=1;i<=n;i++)if(n%i==0){if(i<=k)h[i]=bit[i]-1;else for(rg int j=0;j<=k;j++)h[i]=Md(Md(h[i]+f[i-j-2])+mod-f[i-k-3]);ans+=(ll)h[i]*phi[n/i]%mod;}print(((ans%mod+mod)*pow(n,mod-2)+(n==k))%mod),putchar('\n');}return 0; }總結
Burnside引理知道就好
主要是要求的東西會求
優化復雜度大法好
總結
以上是生活随笔為你收集整理的[bzoj1547]周末晚会的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Burnside引理和Polya定理学习
- 下一篇: [luogu4128][shoi2006