hdu 6021 MG loves string

MG loves string

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

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  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

题意:给定26个小写字母x1,x2,...,x26的字符串作为“密码表”,26个密码分别对应a~z26个小写字母,一个字母进行一次变换,意味着该字母变换成对应的密码,譬如字母b下一次变换应该变成x2,字母x2会变换成x2对应的密码等等(可以知道,经过有限次的变换,每个字母最终还是会变换回来的)。

现在给定一个随机字符串的长度N,并且字符串是由任意的小写字母组成,且该字符串经过有限次变换还是会变换到自身,求任意字符串的变换期望次数。

思路:求期望次数,首先想到的是穷举每一种长度为N的小写字符串(一共26^N种可能),对于每次得到的字符串,求出变换到自身的变换次数,之后求平均。当然这样的效率是极其低下的。每个字母经过有限次变换都是可以变换到自身的,将这个过程中所有经过变换得到的字母组成一个圈,那么这个圈中每一个字母都会变换相同的次数之后变换到自身),这样一来,所有的26个字母就可以被挑拣成几个不同的圈,情况最坏的时候每个字母分别成为一个圈,一共26个圈,有点多,现在继续考虑能否将长度一样的圈挑出来成为一个group,那么这样一共会有多少group呢,最坏的情况,圈的长度从1开始变化,1+2+3+4+5+6+7=28>26,也就是说绝对不可能产生七个长度不同的group,最多6个,这么一来范围就小多了。

现在考虑长度相同的每一个group的性质,可以知道每一个group中的每个字母变换到自身的次数都相同。

那么现在长度为N的随机字符串的每一个位置,都可以由用每一个group中任意一个字符来填充。穷举group的组合情况,对于每一种group的组合情况,随机字符串中的每一位都由这些group中的字符来填充(因为每一个group都至少得派出一个字符来填充随机字符串,所以每一种group的组合情况中group的数量必须小于等于随机串长度N,否则直接排除在这种group的组合),那么由这些group中的字符组成随机串对期望变换次数的贡献=这些group构成的字符串的变换次数change_time*这些group构成的字符串的数量number/(26^N);

其中,change_time=组合中每一个group长度的最小公倍。

p=number/(26^N)则可以由容斥原理得到,举个简单的例子,设Pabc=长度为n的随机串用group a,b,c或其一或其二中字母组成的概率,显然Pabc=((size(a)+size(b)+size(c))/26)^N.那么随机串由group a,b,c组成的概率P=Pabc-Pab-Pac-Pbc+pa+pb+pc;

穷举group的组合情况并累加贡献即可得到最终期望。

AC代码:

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const int N_MAX = 1000000000 + 2,INF= 1000000007;
int N;
string s;
vector<pair<int,int> >loop;//圈的大小<->圈的个数

ll gcd(ll a,ll b) {
    if (b == 0)return a;
    return gcd(b, a%b);
}

ll lcm(ll a,ll b) {
    return a / gcd(a, b)*b;
}

void cal_loop() {
    loop.clear();
    int change[26],vis[26];
    memset(vis,0,sizeof(vis));
    for (int i = 0; i < s.size();i++) {
        change[i] = s[i] - ‘a‘;
    }
    map<int, int>m;
    for (int i = 0; i < 26;i++) {

        if (vis[i])continue;
        vis[i] = true;
        int num = 1;
        int k = change[i];//k代表不断变化的字母
        while (i != k) {
            vis[k] = true;
            num++;//该圈的元素个数加1
            k = change[k];//!!!!!顺序
        }
        m[num]++;
    }
    for (map<int, int>::iterator it = m.begin(); it != m.end();it++) {
        loop.push_back(*it);
    }
}

ll mod_pow(ll x,ll n) {//快速幂运算
    ll res = 1;
    while (n>0) {
        if (n & 1)res = res*x%INF;
        x = x*x%INF;
        n >>= 1;
    }
    return res;
}

ll R_C(vector<int>&loop,int N) {//容斥原理,求由这些圈中的元素组成的长度为N的字符串的数量
    ll permute = 1 << (loop.size());
    ll ans = 0;
    for (int i = 0; i < permute;i++) {
        int num = 0;
        int sum = 0;
        int sign=-1;
        for (int j = 0; j < loop.size(); j++) {
            if (i&(1 << j)) {
                num++;//num记录利用到的圈的个数
                sum += loop[j];//利用到的字符的总个数
            }
        }
            if (num % 2 == loop.size() % 2)//!!!!!!!!
                sign = 1;
            ans =(ans+((sign*mod_pow(sum, N))%INF+INF)%INF)%INF;
    }
    return ans;
}

ll solve(int N) {
    cal_loop();
    vector<int>vec;
    ll ans = 0;
    for (int i = 0; i < (1<<loop.size());i++) {
        ll change_time=1;
        vec.clear();
        for (int j = 0; j < loop.size();j++) {
            if (i&(1 << j)) {
                vec.push_back(loop[j].first*loop[j].second);
                change_time = lcm(change_time, loop[j].first);
            }
        }
        if (vec.size() > N)continue;//挑选出来的圈的个数不能超过字符串长度
    ll number = R_C(vec, N);
    ans = (ans + change_time*number) % INF;
    }
    return ans;
}

int main() {
    int T;
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&N);
        cin >> s;
        printf("%lld\n",solve(N));
    }
    return 0;
}
时间: 2024-10-02 08:40:38

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个字母组成的排列. M

【HDU 6020】 MG loves apple (乱搞?)

MG loves apple Accepts: 20 Submissions: 693 Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) 问题描述 MGMG是一个财富爆表的男孩子.他拥有N(1<=N<=100000)N(1<=N<=100000)个苹果,每个苹果上标有一个数字00~99,代表它的价值. 一个合法的数字是不含有前导零的,这nn个苹果恰好排成了一个合法的N

HDU 4873 ZCC Loves Intersection(JAVA、大数、推公式)

在一个D维空间,只有整点,点的每个维度的值是0~n-1 .现每秒生成D条线段,第i条线段与第i维度的轴平行.问D条线段的相交期望. 生成线段[a1,a2]的方法(假设该线段为第i条,即与第i维度的轴平行)为,i!=j时,a1[j]=a2[j],且随机取区间[0,n-1]内的整数.然后a1[i],a2[i]在保证a1[i]<a2[i]的前提下同样随机. 由于D条线段各自跟自己维度的轴平行,我们可以转换成只求第i个维度与第j个维度的相交期望,然后乘以C(2,n)就好了 显然线段[a1,a2]和线段[

hdu 5677 ztr loves substring 二维费用背包+回文

题目链接: hdu 5677 ztr loves substring 官方题解: //这部分是错的(首先,对于每一个串i跑一次manancher,令g[i][j]=p[j]-1g[i][j]=p[j]−1 这样,g就存储了所有的回文子串的长度 为了方便,把g降到一维表示) 首先,因为字符串长度较小,所以直接二重for循环找就好了,用一个数组 g记录各个回文串的长度 不妨把每一个子串抽象成一个物品 费用为二维的,即{长度,1} 价值是Bool型的 这样就成了一个二维判断可行性费用背包问题 设f(i

HDU 4876 ZCC loves cards _(:зゝ∠)_ 随机输出保平安

GG,,,g艹 #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <vector> #include <queue> #include <math.h> using namespace std; vector<int>G[21][7];//G[i][j] 表示n=i k=j的情况下 二进

hdu 4882 ZCC Loves Codefires(贪心)

# include<stdio.h> # include <algorithm> # include <string.h> using namespace std; struct node { int v; int t; }; struct node a[100010]; bool cmp(node a,node b) { return a.v *a.t+(a.v+b.v)*b.t<b.v*b.t+(a.v+b.v)*a.t; } int main() { int

JAVA hdu 4882 ZCC Loves Codefires

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4882 题解:参考题后Discuss javaherongwei 的讲解 考察序列中相邻的两题i, j(i在前).交换它们后,解出它们之前的题目所带来的时间对答案的贡献是不变的,它们对它们后面的题目的贡献也是不变的,其他题目之间对答案的贡献自然也是不变的.唯一的变化就是,原来的EiKj一项变成了EjKi一项.那么,为了使答案变优,需要满足的条件是EjKi≤EiKj.也即Ei/Ki≥Ej/Kj.那么,最

hdu 4882 ZCC Loves Codefires (贪心 推导)

题目链接 做题的时候凑的规律,其实可以 用式子推一下的. 题意:n对数,每对数有e,k, 按照题目的要求(可以看下面的Hint就明白了)求最小的值. 分析:假设现在总的是sum, 有两个e1 k1 e2 k2 则先选e1 为 (sum+e1)*k1+(sum+e1+e2)*k2 先e2: (sum+e2)*k2 + (sum+e1+e2)*k1. 比较两个式子发现不同的部分分别是 e1*k2   e2*k1; 比较大小移向 e1/k1  e2/k2, 那个小,就选那个,能达到最小. 官方题解:

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理)

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理) 题意:给出若干个模式串,总长度不超过40,对于某一个字符串,它有一个价值,对于这个价值的计算方法是这样的,设初始价值为V=1,假如这个串能匹配第k个模式串,则V=V*prime[k]*(i+len[k]),其中prime[k]表示第k个素数,i表示匹配的结束位置,len[k]表示第k个模式串的长度(注意,一个字符串可以多次匹配同意个模式串).问字符集为'A'-'Z'的字符,组成的所有的长为L的字符串,