• HDU-6021 MG loves string


    题目描述

    给定(26)个小写字母(x1,x2,...,x26)的字符串作为“密码表”,

    (26)个密码分别对应(a-z,26) 个小写字母,一个字母进行一次变换,

    意味着该字母变换成对应的密码,譬如字母(b)下一次变换应该变成(x2)

    (可以知道,经过有限次的变换,每个字母最终还是会变换回来的)。

    求长度为(n)的任意字符串的变换成自身的期望次数。

    Input

    第一行读入一个(T(T<=10))

    表示数据的组数

    然后一个(n),表示字符串长度((n<=1000000000))

    然后是一个密码表

    Output

    输出答案为期望乘上(26^n)并模上(1e9+7)的结果

    Sample Input

    2
    2
    abcdefghijklmnpqrstuvwxyzo
    1
    abcdefghijklmnopqrstuvwxyz
    

    Sample Output

    5956
    26
    

    容斥好题!!!

    我们发现实际上题目要我们求的答案就是每个串变为自身的次数之和。

    对于每个字母我们都可以求出他们变化为自身的次数(A_i)

    对于一个字符串,它变为自身的次数实际上就是该字符串中所有(A_i)(lcm)

    如果暴力搜索的话,我们会是搜索每个字母是否在该字符串中出现,最后统计方案数。

    这样的话,时间复杂度为(O(2^{26}*log_n)*T),无法承受。

    我们发现对于一些(A_i)相等的字母我们选择其中的任意一个就行了,而(A_i)不同的字母一共也就最多有(1+2+3+4+5+6=28>26),即(6)种。

    我们发现复杂度为(O(2^6*log_n*T))是完全可以接受的。


    首先,我们先将所有不同的(A_i)求出来,共(tot)个不同的取值。

    对于当前考虑的(A_i)的集合(s),我们需要知道(n)个数放入其中,并且每个集合中都有放数的方案数。

    即我们需要知道(n)个数,放入(m)个集合并且每个集合至少要有一个数的方案数。

    这个我们可以利用容斥来解决。

    对于当前的集合(s),一共有(cnt)个字母,那么一共有(cnt^n)种情况。

    我们发现,这(cnt^n)种情况中,有一大部分都是含有空集合的情况的。

    于是我们可以枚举其子集,将答案减去其子集的答案即可。

    于是我们对于一种状态,我们求出了方案数(dp[s])


    接下来,我们就可以直接开始爆搜的,对于当前的状态(s),我们维护其(lcm),最后答案累加上(lcm*dp[s])

    代码如下

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define int long long
    #define reg register
    #define Raed Read
    #define clr(a,b) memset(a,b,sizeof a)
    #define Mod(x) (x>=mod)&&(x-=mod)
    #define debug(x) cerr<<#x<<" = "<<x<<endl;
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)>(b)?(b):(a))
    #define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
    #define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
    #define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
    #define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
    #pragma GCC target("avx,avx2,sse4.2")
    #pragma GCC optimize(3)
    
    inline int Read(void) {
    	int res=0,f=1;
    	char c;
    	while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    	do res=(res<<3)+(res<<1)+(c^48);
    	while(c=getchar(),c>=48&&c<=57);
    	return f?res:-res;
    }
    
    template<class T>inline bool Min(T &a, T const&b) {
    	return a>b?a=b,1:0;
    }
    template<class T>inline bool Max(T &a, T const&b) {
    	return a<b?a=b,1:0;
    }
    const int N=1e5+5,M=7e6+5,mod=1e9+7;
    
    bool MOP1;
    
    int n;
    
    inline int gcd(int a,int b) {
    	while(b^=a^=b^=a%=b);
    	return a;
    }
    
    char S[30];
    
    int tot,Ans,cnt[28],vis[28],Sum[10],A[10],dp[(1<<6)+5];
    
    void dfs(int x,int s,int Lcm) {
    	if(x==tot+1) {
    		(Ans+=dp[s]*Lcm)%=mod;
    		return;
    	}
    	dfs(x+1,s,Lcm);
    	dfs(x+1,s|(1<<(x-1)),Lcm/gcd(Lcm,A[x])*A[x]);
    }
    
    inline int Pow(int x,int y) {
    	int res=1;
    	while(y) {
    		if(y&1)res=(res*x)%mod;
    		x=(x*x)%mod,y>>=1;
    	}
    	return res;
    }
    
    bool MOP2;
    
    inline void _main() {
    	int T=Read();
    	while(T--) {
    		int n=Read();
    		tot=Ans=0,scanf("%s",S+1);
    		clr(cnt,0),clr(Sum,0),clr(vis,0);
    		rep(i,1,26) if(!vis[i]) {
    			int temp=0,Now=i;
    			while(!vis[Now])temp++,vis[Now]=1,Now=S[Now]-'a'+1;
    			cnt[temp]+=temp;
    		}
    		rep(i,1,26)if(cnt[i])A[++tot]=i,Sum[tot]=cnt[i];
    		ret(i,1,1<<tot) {
    			int res=0;
    			rep(j,1,tot)if(i&(1<<(j-1)))res+=Sum[j];
    			dp[i]=Pow(res,n);
    		}
    		ret(i,1,1<<tot) {
    			int res=0;
    			for(int j=i; j; j=i&(j-1))if(j!=i)res+=dp[j],Mod(res);
    			dp[i]-=res,(dp[i]<0)&&(dp[i]+=mod);
    		}
    		dfs(1,0,1);
    		printf("%d
    ",Ans);
    	}
    }
    
    signed main() {
    	_main();
    	return 0;
    }
    
  • 相关阅读:
    Object-Oriented Programming Summary Ⅱ
    Object-Oriented Programming Summary Ⅰ
    自己查与写的批量比较bash
    C#可为空引用类型 -0007
    C#类型系统 -0006
    C#类型 -0005
    C# Main方法返回值 -0004
    C# Main方法参数 -0003
    C# Main方法 -0002
    C# Hello World -0001
  • 原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11438156.html
Copyright © 2020-2023  润新知