【JZOJ100207】【20190705】決心

題目

你須要構造一個排列c++

初始時\(p_i=i\),一次操做定義爲:spa

  • 選擇一些\((x_i,y_i)\),知足每一個數字只能出現一次code

  • 依次交換\(p_{x_i},p_{y_i}\)blog

定義一個排列 \(P\) 的最少交換次數爲\(f(P)\)it

如今 \(P\)\(k\) 個位置的排列順序是未知的,定義某一種肯定順序的方案是\(P'\)io

\(\sum f(P')\)class

\(1 \le n \le 10^6 \ , \ k \le min(12,n)\)im

題解

  • 首先操做次數不會超過2,考慮每個輪換static

    img

    img

  • 考慮一個排列\(P\),它的貢獻是:
    1.若是全部輪換的大小<=2 ,最少的步數爲0/1,貢獻爲0img

    2.若是存在輪換的大小>2,最少的步數爲2,考慮貢獻

    大小不一樣的輪換不會互相影響

    大小相同的輪換能夠兩兩拼在一塊兒,也能夠單獨存在

    寫成dp即$h_{i,j}  =  h_{i,j-1}i + h_{i,j-2}(j-1)i $

    若是大小爲\(i\)的輪換有\(m_i\)個,總貢獻爲\(\prod h_{i,m_i}\)

  • 若是肯定的點指向不肯定的點存在一條鏈\(l\),那麼就把不肯定的點的大小看作\(|l|+1\)

  • 預處理出全部的\(h\),這樣就能夠只考慮不肯定的點的排列

  • 因爲貢獻只和輪換的大小有關,能夠枚舉集合劃分再把貢獻乘以一個圓排列

  • 時間複雜度\(O(n \ log \ n + bell(k) \ k)\)

Code

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1000010,mod=1e9+7;
int n,k,p[N],q[N],cnt[N],pos[N],a[N],b[N],c[N],bl[N],iv[N],vis[N],T,preans,precnt[N],ans,fac[N],prefg1,prefg2,Bell;
vector<int>h[N];
void inc(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int pw(int x,int y){
    int re=1;
    for(;y;y>>=1,x=1ll*x*x%mod)
        if(y&1)re=1ll*re*x%mod;
    return re;
}
void pre(){
    for(int i=fac[0]=1;i<=n;++i){
        fac[i]=1ll*fac[i-1]*i%mod;
        int lim=n/i;
        h[i].pb(1);h[i].pb(i);
        for(int j=2;j<=lim;++j){
            h[i].pb((h[i][j-1]+1ll*h[i][j-2]*(j-1)%mod)*i%mod);
        }
    }
}
void calc(int tot){
    static int st[N],tp;
    ++T;tp=0;
    for(int i=1;i<=tot;++i)b[i]=0,c[i]=0;
    for(int i=1;i<=k;++i)b[bl[i]]++,c[bl[i]]+=a[i];
    int re=1,fg1=prefg1,fg2=prefg2;
    for(int i=1;i<=tot;++i){
        re=1ll*re*fac[b[i]-1]%mod;
        cnt[c[i]]++;fg1|=c[i]>1;fg2|=c[i]>2;
        if(vis[c[i]]!=T)vis[c[i]]=T,st[++tp]=c[i];
    }
    if(!fg1)re=1;
    if(fg2)re=1ll*re*preans%mod;
    for(int i=1;i<=tp;++i){
        int x=st[i];
        if(fg2)re=1ll*re*iv[x]%mod*h[x][cnt[x]]%mod;
        cnt[x]=precnt[x];
    }
    inc(ans,re);
}
void dfs(int x,int y){
    if(x==k+1){calc(y);return;}
    for(int i=1;i<=y+1;++i){
        bl[x]=i;
        dfs(x+1,max(i,y));
    }
}
int main(){
    freopen("determination.in","r",stdin);
    freopen("determination.out","w",stdout);
    scanf("%d%d",&n,&k);k=0;
    for(int i=1;i<=n;++i){
        scanf("%d",&p[i]);
        if(p[i])q[p[i]]=i;else pos[++k]=i;
    }
    for(int i=1;i<=k;++i){
        int len=1;vis[pos[i]]=1;
        for(int j=q[pos[i]];j;j=q[j])vis[j]=1,len++;
        a[i]=len;
    }
    for(int i=1;i<=n;++i)if(!vis[i]){
        int len=1;vis[i]=1;
        for(int j=p[i];j!=i;j=p[j])vis[j]=1,len++;
        cnt[len]++;prefg2|=len>2;prefg1|=len>1;
    }
    pre();
    preans=1;
    for(int i=1;i<=n;++i){
        precnt[i]=cnt[i];
        preans=1ll*preans*h[i][cnt[i]]%mod;
        iv[i]=pw(h[i][cnt[i]],mod-2);
        vis[i]=0;
    }
    dfs(1,0);
    cout<<ans<<endl;
    return 0;
}
相關文章
相關標籤/搜索