hdu 2243

1.解释:

先构造矩阵A,AnA^n表示含有词根长度为n的单词数量,长度不小于n且含词根的单词数量X=A+A2+...+AnX=A+A^2+...+A^n,单词总数sum=26+262+...+26nsum=26+26……2+...+26^n,根据题意,需要求长度不小于n且不含词根的单词数量,则ans=sum-X;

可以参考二分幂的方法:先求出(A+A2+A3An/2)(A+A^2+A^3……A^{n/2})【记为B】和An/2A^{n/2}的值,则X=(An/2+E)BX=(A^{n/2}+E)*B,E为单位阵。算数的也是同样的方法。将运算过程中的变量和矩阵的元素定义为unsigned long long,就能实现自动对2^64自动取MOD.注意:因为我的代码是递归的形式,求完n/2,再求n,故要讨论n是奇还是偶

2.代码:

#include<cstdio>
#include<cstring>
#include<iostream> 
#include<queue>
#define ull unsigned long long
using namespace std;
int ch[35][26],f[35],sz;
int val[35];
struct Trie{
    Trie(){ sz = 0; memset(ch,0,sizeof(ch)); memset(val,0,sizeof val);};
    void insert(char *s){
        int u=0;
        for(int i=0;s[i];++i){
            int c=s[i]-&#39;a&#39;;
            if(!ch[u][c]) ch[u][c] = ++sz;
            u=ch[u][c];
        }
        val[u]=1;
    }
};
void getFail(){
    queue<int>q; int u;
    f[0] = 0;
    for(int c=0;c<26;++c){
        u = ch[0][c];
        if(u)  { f[u]=0; q.push(u);}
    }
    while(!q.empty()){
        int r=q.front(); q.pop();
        for(int c=0;c<26;++c){
            u=ch[r][c];
            if(!u){
                ch[r][c]=ch[f[r]][c];
                continue;
            }
            q.push(u);
            f[u] = ch[f[r]][c];
            val[u]|=val[ch[f[r]][c]];
        }
    }
}
struct mat {
    ull matrix[35][35];
    mat() {
        memset(matrix,0,sizeof(matrix));
    }
}a;
mat one() {
	mat res;
	for(int i=0; i<=sz; ++i) res.matrix[i][i]=1;
    return res;
}
mat zero() {
	mat res;
	memset(res.matrix,0,sizeof(res.matrix));
	return res;
}
mat operator*(const mat &m1,const mat &m2){
    mat m;
    for(int i=0; i<=sz; ++i){
        for(int j=0; j<=sz; ++j){
            for(int k=0; k<=sz; ++k){
                m.matrix[i][j]+=m1.matrix[i][k]*m2.matrix[k][j];
            }
        }
    }
    return m;
}
mat operator+(const mat &m1,const mat &m2){
    mat m;
    for(int i=0; i<=sz; ++i){
        for(int j=0; j<=sz; ++j){
                m.matrix[i][j]=m1.matrix[i][j]+m2.matrix[i][j];
        }
    }
    return m;
}
pair<ull,ull> bin(int k) {
	if(k==1)	return make_pair(26,26);
	else if(k==0)	return make_pair(0,1);
	pair<ull,ull> tmp,res;
	tmp=bin(k>>1);
	res.first=(tmp.second+(ull)1)*tmp.first;
	res.second=tmp.second*tmp.second;
	if(k&1) {
		res.second=res.second*26;
		res.first=res.first+res.second;
	}
	return res;
}
pair<mat,mat> bing(int k) {
	if(k==1)	return make_pair(a,a);
	else if(k==0)	return make_pair(zero(),one());
	pair<mat,mat> tmp,res;
	tmp=bing(k>>1);
	res.first=(tmp.second+one())*tmp.first;
	res.second=tmp.second*tmp.second;
	if(k&1) {
		res.second=res.second*a;
		res.first=res.first+res.second;
	}
	return res;
}
char str[6];
int n,l;
int main(){
    while(~scanf("%d%d",&n,&l)) {
    	Trie trie;  ull ans=0;
    	a=zero();
    	for(int i=0;i<n;++i) {
    		scanf("%s",str);
    		trie.insert(str);
		}
		getFail();
		for(int i=0;i<=sz;++i) {
			if(val[i])	continue;
			for(int j=0;j<26;++j) {
				if(val[ch[i][j]])	continue;
				++a.matrix[i][ch[i][j]];
			}
		}
		a=bing(l).first;
		for(int i=0;i<=sz;++i)	ans+=a.matrix[0][i];
		ans=bin(l).first-ans;
		printf("%llu\n",ans);
	}
}

(写了getfail函数却没有调用它,但是过了一些案例,一提交就wa,wa了一天,wa得明明白白的)

hdu 2825

1.解释:
dp[i][j][k]表示走到结点j时字符串长度为i,包含k种单词,每一种状态都是从上一个状态转变得来的。 从结点j走向儿子a...z,结点为ret=ch[j][d]--0<=d<26,这一步的后果是字符串长度加一,该字符如果不是单词的结尾,k不变,如果是,但是已经包含了,k也不变;k=k|val[ret] 也就是说下一个状态是:dp[i+1][ret][k|val[ret]] 初始状态:dp[0][0][0]=1; 状态转移方程:dp[i+1][ret][k|val[ret]]=(dp[i+1][ret][k|val[ret]]+dp[i][j][k])%mod;

二进制得小心点,不小于k个单词不能直接int j=(1< < cnt)-1,比如k=3,j=7,二进制是111,但是++j后j=8,二进制1000,这个j是不符和题意的。所以还需要继续判断这个j是不是符合题意

2.代码:
#include<bits/stdc++.h> 
#define mod 20090717
using namespace std;
int ch[105][26],f[105],sz,td,ans;
int val[105],dp[30][105][1024];
/*dp[i][j][k]中,k最大为(1<<m)-1*/
struct Trie{
    Trie(){ sz = 0; td=0; memset(ch,0,sizeof(ch)); memset(val,0,sizeof val);memset(dp,0,sizeof dp);};
    void insert(char *s){
        int u=0;
        for(int i=0;s[i];++i){
            int c=s[i]-&#39;a&#39;;
            if(!ch[u][c]) ch[u][c] = ++sz;
            u=ch[u][c];
        }
        val[u]=1<<(td++);
    }
};
int getFail(){
    queue<int>q; int u;
    f[0] = 0;
    for(int c=0;c<26;++c){
        u = ch[0][c];
        if(u)  { f[u]=0; q.push(u);}
    }
    while(!q.empty()){
        int r=q.front(); q.pop();
        for(int c=0;c<26;++c){
            u=ch[r][c];
            if(!u){
                ch[r][c]=ch[f[r]][c];
                continue;
            }
            q.push(u);
            f[u] = ch[f[r]][c];
            val[u]|=val[f[u]];
        }
    }
}
int num(int x){
    int ans = 0;
    while(x){
        if(x&1) ans++;
        x /= 2;
    }
    return ans;
}
char str[11];
int n,m,cnt;
int main(){
    while(~scanf("%d%d%d",&n,&m,&cnt)&&n) {
        Trie trie;    ans=0;
        for(int i=0;i<m;++i) {
            scanf("%s",str);
            trie.insert(str);
        }
        getFail();
        dp[0][0][0]=1;
        for(int i=0;i<n;++i)
            for(int j=0;j<=sz;++j)
                for(int k=0;k<(1<<m);++k)
                    if(dp[i][j][k])  for(int d=0;d<26;++d) {
                        int ret=ch[j][d];
                        dp[i+1][ret][k|val[ret]]=(dp[i+1][ret][k|val[ret]]+dp[i][j][k])%mod;
                    }
        for(int i=0;i<=sz;++i)
            for(int j=(1<<cnt)-1;j<(1<<m);++j)
                if(dp[n][i][j]&&num(j)>=cnt)
                    ans=(ans+dp[n][i][j])%mod;
        printf("%d\n",ans);
    }
}