HNOI2008 GT考试 BZOJ1009 洛谷P3193

此题主要思路是dp,但dp有多种写法。

我们用f[i][j]表示现在已经有i个数字了,而且最后j个数字是不幸数字前j个数字。(保证不包含不吉利序列)

为了防止dp的意义出现歧义,我们让对于同一个状态j要竟可能大;

比如 1212是不幸数字,那么551212是f[6][4];

这样的话我们可以发现f[0][0]=1;

ans=f[n][k] ,k∈[0,m-1] ∪Z(若k=m,那么已经形成一个不吉利序列)

这样的话我们可以从f[0][0]地推;

每局这个状态最后放一个什么数字,然后kmp求j

a[j][k]表示j个不幸之后放k产生的新的j

       a[j][k]这个可以用kmp解决

这样可以得到转移方程f[i+1][a[j][k]]=(f[i+1][a[j][k]]+f[i][j])%mo;

意为已经知道f[i][j],最后j个是不吉利数字的前j个,加上一个数字k,最多形成不吉利序列的前a[j][k]个,所以可以通过f[i][j]的状态转移到f[i+1][a[j][k]]的状态。

此题满分需要用矩阵优化dp,但修改以后占比很小,此处不讲,想学习的同学可以上网查题解。

下面是网上使用范围更多的一种转移方程(但没有标程)。

设f[i][j]表示内容同上

f[i][j]的准确含义: f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀 (同上)

 否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况

状态转移:

f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其不吉利后缀k(长为k的后缀)后面新添一位t,加好后之后这个i位数的 与不吉利数前缀相同的最长后缀长度为j

我们定义a[k][j]就表示有几种方法可以通过加一个数字使长度为k的后缀变为长度为j的后缀(最长后缀),可以用kmp算法预处理出来,

当i>=1时:

f[i][j]=f[i-1][0]*a[0][j]+f[i-1][1]*a[1][j]+…+f[i-1][m-1]*a[m-1][j]

      比如:还是假设不吉利数为123124,那么 f[i][3]=f[i-1][2]+f[i-1][5],因为 f[i][3]的后缀为*****123,f[i-1][2]末尾的*****12不能是**12312(详见定义),所以需要f[i-1][5](***12312)补充

但若不吉利数为123123,那么 f[i][3]=f[i-1][2],因为 f[i][3]末尾的*****123不能是**123123(包含不吉利序列)

i==0时:f[0][0]=1;

这样就可以不重不漏地计数了。

其实这两个题解思路大同小异,打过以后发现代码也很像

90分

#include <bits/stdc++.h>

#define N 25

#define M 10005

using namespace std;

inline int read(){

    int ret=0,f=1;char c=getchar();

    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;

    for(;isdigit(c);c=getchar())ret=ret*10+c-'0';

    return ret*f;
}
int f[2][N],p[N],a[N][N],n,m,mo,ans;
char c[N];
inline int dfs(int j,int k){
    k+=48;
    while(~j&&c[j+1]!=k)j=p[j];
    return j+1;
}
int main(){
	freopen("lottery.in","r",stdin);
	freopen("lottery.out","w",stdout);
	register int ll;
    scanf("%d%d%d%d",&n,&m,&mo,&ll);
    scanf("%s",c+1);
    p[0]=-1;register int j=-1;
    for(register int i=1;i<=m;p[i++]=++j){
        while(~j&&c[i]!=c[j+1])j=p[j];
    }
    for(register int j=0;j<=m-1;j++)
        for(register int i=0;i<=9;i++)
            a[j][i]=dfs(j,i);
    f[0][0]=1;
    for(register int i=0;i<n;++i){
        for(register int j=0;j<m;++j){
            for(register int k=0;k<=9;++k){
                f[(i+1)&1][a[j][k]]=(f[(i+1)&1][a[j][k]]+f[i&1][j])%mo;
            }
            f[i&1][j]=0;
        }
    }
    for(register int i=0;i<m;++i)(ans+=f[n&1][i])%=mo;
    int s=ans % mo;
    if (s==0) s+=mo;
    printf("0.");
    register int t=1;
    for (register int i=1;i<ll;i++)
    {
        t*=10;
        printf("%d",t/s);
        t%=s;
    }
    t*=10;
    int t1=t/s;
    t%=s;
    t*=10;
    if (t/s>=5) t1++;
    printf("%d\n",t1);
    return 0;
}

100分

 
 
#include<bits/stdc++.h>
using namespace std;
int n,m,mod,s,nxt[21],ll;
char c[21];
struct matrix
{
    int f[21][21];
    matrix()
    {
        memset(f,0,sizeof(f));
    }
}a;
matrix operator*(const matrix &a,const matrix &b)
{
    matrix c;
    for(register int i=0;i<m;i++)
        for(register int j=0;j<m;j++)
            for(register int k=0;k<m;k++)
                c.f[i][j]=(a.f[i][k]*b.f[k][j]+c.f[i][j]) % mod;
    return c;
}
int search(int j,int k)
{
    while (j!=-1 && c[j+1]!=char(k+48)) j=nxt[j];
    return j+1;
}
matrix qpow(matrix now,int x)
{
    if (x==1LL) return now;
    matrix c=qpow(now,x>>1);
    if (x & 1) return c*c*now;
    else return c*c;
}
int main()
{
	freopen("lottery.in","r",stdin);
	freopen("lottery.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&mod,&ll);
    scanf("%s",c+1);
    register int j=-1;
    nxt[0]=-1;
    for (register int i=1;i<=m;nxt[i++]=++j)
        while (j!=-1 && c[i]!=c[j+1]) j=nxt[j];//kmp
    for (register int j=0;j<m;j++)
        for(register int i=0;i<=9;i++) a.f[j][search(j,i)]++;
    a=qpow(a,n);
    for (register int i=0;i<m;i++) s=(s+a.f[0][i]) % mod;
    if (s==0) s+=mod;
    printf("0.");
    int t=1;
    for (register int i=1;i<ll;i++)
    {
        t*=10;
        printf("%d",t/s);
        t%=s;
    }
    t*=10;
    int t1=t/s;
    t%=s;
    t*=10;
    if (t/s>=5) t1++;
    printf("%d\n",t1);
}

猜你喜欢

转载自blog.csdn.net/fatalzyc/article/details/80260280
今日推荐