HDU6021 MG loves string 【容斥定理】

MG loves string

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 99    Accepted Submission(s): 31


Problem Description
MG is a busy boy. And today he's burying himself in such a problem:

For a length of N, a random string made of lowercase letters, every time when it transforms, all the character i will turn into a[i].

MG states that the a[i] consists of a permutation . 

Now MG wants to know the expected steps the random string transforms to its own.

It's obvious that the expected steps X will be a decimal number.
You should output X∗26Nmod 1000000007. 


Input
The first line is an integer T which indicates the case number.(1<=T<=10) 

And as for each case, there are 1 integer N in the first line which indicate the length of random string(1<=N<=1000000000).

Then there are 26 lowercase letters a[i] in the next line.


Output
As for each case, you need to output a single line.

It's obvious that the expected steps X will be a decimal number.
You should output X∗26Nmod 1000000007.


Sample Input
2
2
abcdefghijklmnpqrstuvwxyzo
1
abcdefghijklmnopqrstuvwxyz


Sample Output
5956
26


Source
BestCoder Round #93

假设字符串str第i个位置的字母 经过 ai 次变换能变回自身
那str需要 lcm(a0,a1,a2,.......,an1) 次变换,才能变回自身

显然 从字母 α 变回 α 途径的字母 ,组成一个环
26个字母组成若干个不相交的环

枚举每个环有没有被选择
如果环 c1,c2,...cn 被选择了 那这种状态对答案的贡献 =lcm(c1,c2,....,cn)

显然这个数量可以容斥求出:
Pabc=na,b,c
Pabc=((size(a)+size(b)+size(c)26)n
a,b,c=26n(PabcPabPacPbc+Pa+Pb+Pc)

然后….tle

最坏情况下 有26个环 , 226=67108864
而1+2+3+4+5+6+7=28 > 26,也就是说 大小不一样的环 不会超过6个
而lcm2个相同的数是毫无意义的
如果将相同大小的环合并在一起 , 就可以压缩为 26

然后愉快得ac了….

#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("/home/lu/Documents/r.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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值