1. 程式人生 > >hdu 6021 MG loves string (一道容斥原理神題)(轉)

hdu 6021 MG loves string (一道容斥原理神題)(轉)

MG loves string    Accepts: 30    Submissions: 67  Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) 問題描述 MGMG是一個很忙碌的男孩子。今天他沉迷於這樣一個問題:

對於一個長度為NN的由小寫英文字母構成的隨機字串,當它進行一次變換,所有字元ii都會變成a[i]a[i]。

MGMG規定所有a[i]a[i]構成了2626個字母組成的排列。

MGMG現在需要知道這個隨機串變換到自身的期望變換次數。請你輸出期望答案乘上26^n26 ​n ​​ 以後模 10000000071000000007 的結果。

MGMG認為這件事非常容易,不屑於用計算機解決,於是運用他高超的人類智慧開始進行計算。作為一名旁觀者,你也想挑戰MGMG智慧,請你寫個程式,計算答案。 輸入描述 第一行一個整數TT,代表資料組數(1 <=T<=101<=T<=10)。

接下來,對於每組資料——

第一行一個整數NN,表示給定的隨機串長度(1<=N<=10000000001<=N<=1000000000)。

第二行2626個字母,表示a_ia ​i ​​ 序列 輸出描述 對於每一組資料,輸出一行。

顯然,這個期望是一個實數。請你輸出它乘上26^N26 ​N ​​ 以後模 10000000071000000007 的結果 輸入樣例 2 2 abcdefghijklmnpqrstuvwxyzo 1 abcdefghijklmnopqrstuvwxyz 輸出樣例 5956 26  

題解請參考這位大神:

他的程式碼:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<string.h>
#include<math.h>
#include<list>

using namespace std;

#define ll long long
#define pii pair<int,int>
const int inf = 1e9 + 7;

char to[27];
bool vis[27];
vector<pii>loop;//fist 迴圈節長度為first 有second個迴圈節長度為first

void calLoop(){
    for(int i=0;i<26;++i){
        to[i]-='a';
    }
    map<int,int>mp;
    fill(vis,vis+27,0);
    loop.clear();
    for(int i=0;i<26;++i){
        if(vis[i]==0){
            vis[i]=1;
            int ans=1;
            int x=to[i];
            while(x!=i){
                vis[x]=1;
                ++ans;
                x=to[x];
            }
            mp[ans]+=1;
        }
    }
    for(auto it=mp.begin();it!=mp.end();++it){
        loop.push_back(*it);
    }
}

ll lcm(ll a,ll b){
    ll tmp=__gcd(a,b);
    return a/tmp*b;
}

ll quickMulti(ll a,ll n){
    ll ans=1;
    ll t=a%inf;
    while(n){
        if(n&1){
            ans=(ans*t)%inf;
        }
        t=(t*t)%inf;
        n>>=1;
    }
    return ans;
}

inline ll mod(ll x){
    return (x%inf+inf)%inf;
}

ll f(vector<int>&vec,int n){//容斥
    ll ans=0;
    int nv=vec.size();
    for(int i=1,end=1<<nv;i<end;++i){
        ll flag=-1;
        int sum=0,num=0;
        for(int j=0;j<nv;++j){
            if((1<<j)&i){
                sum+=vec[j];
                ++num;
            }
        }
        if(num%2==nv%2){
            flag=1;
        }
        ll t=quickMulti(sum,n);
        ans=(ans+mod(flag*t))%inf;
    }
    return ans;
}

ll slove(int n){
    calLoop();
    ll ans=0;
    vector<int>vec;//當前選擇了的迴圈節包含的字母數
    for(int i=1,end=1<<loop.size();i<end;++i){//列舉迴圈節的狀態
        ll ans1=1;//選擇了這幾個迴圈節的貢獻
        vec.clear();
        for(int j=0;j<loop.size();++j){
            if((1<<j)&i){
                ans1=lcm(ans1,loop[j].first);
                vec.push_back(loop[j].first*loop[j].second);
            }
        }
        if(vec.size()>n){
            continue;
        }
        ll ans2=f(vec,n);
        ans=(ans+ans1*ans2)%inf;
    }
    return ans;
}

int main()
{
//freopen("in.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d%s",&n,to);
        printf("%lld\n",slove(n));
    }
    return 0;
}