2019.9.10

\(1.bzoj\) \(1008\) 越狱

题意:

\(n\)个位置,\(m\)种数,每个位置填一个数,使得有相邻两位置的数相同,问有多少种方案。每种数的个数都是无限的。

分析:

明显的排列组合问题

我们现在先不考虑相邻位置的数相同的条件,则一共有
\[ m^n \]
种方案,再减去每个相邻位置的数都不相同的方案总数。

怎么算呢?

第一个人有m种选择,第二个人因为要与第一个人不同,故有m-1种选择

第三个人因为要与第二个人不同,故有m-1(除去第二个人的那个数)

...

第n个人有m-1种选择,故共有
\[ m^n-m*(m-1)^{n-1} \]
种方案

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define ll long long

const int mod=1e5+3;
ll fpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    ll n,m;
    scanf("%lld%lld",&m,&n);//注意输入顺序
    ll res=(fpow(m,n)-m*fpow(m-1,n-1))%mod;
    while(res<mod) res+=mod;//要注意负数的处理
    printf("%d\n",res%mod);
}

收获:补集转化,数学分析,快速幂

\(2.bzoj\) \(1009\) \(GT\)的考试

题意:

阿申准备报名参加\(GT\)考试,准考证号为\(N\)位数\(X1X2....Xn(0<=Xi<=9)\),他不希望准考证号上出现不吉利的数字。他的不吉利数学\(A1A2...Am(0<=Ai<=9)\)有M位,不出现是指\(X1X2...Xn\)中没有恰好一段等于\(A1A2...Am.\) \(A1\)\(X1\)可以为\(0\)

分析:

首先可以发现这是一个动态规划的题,我们设\(f(i,j)\)表示前i个字符匹配到了不吉利字符的第\(j\)位的方案数,则答案为
\[ ans=\sum_{j=0}^{M-1}f[n][j] \]
为什么是到\(M-1\)呢?因为我们不能出现不吉利数字。

这样一个字符串匹配,让我们很容易想到\(kmp\)或者\(AC\)自动机,由于它只有一个匹配串和一个文本串,没必要用\(AC\)自动机,用\(kmp\)就可以了

\(f(i,j)\)可以转移到\(1\).\(f(i+1,j+1)2.f(i+1,next[j]+1)3.f(i+1,0)\)

而:
\[ f[i][j]=\sum_{k=0}^{M-1} f[i-1][k]*a[k][j] \]
怎么算\(a(k,j)\)呢?我们枚举在准考证上第\(i+1\)位可能出现的字符,然后看\(f(i,j)\)能转移到哪里去,接着就在转移矩阵上的这个转移路径上\(+1\)

这样的形式非常像矩阵乘法,再看看我们的数据范围\(n<=1e9\),所以我们要使用矩阵快速幂来优化

为什么可以用矩阵快速幂来优化呢?因为
\[ \sum_{k=0}^{M-1}a[k][j] \]
是一个定值,所以我们预处理出这部分.
\[ f(i,j)=f(i-1,0)*a(0,j)+f(i-1,1)*a(1,j)+f(i-1,2)*a(2,j)... \]
我们发现a的列数都是相同的,所以把f数组也抽象为一个矩阵
\[ (f[i][0]\quad f[i][1]\quad...)=(f[i-1][0]\quad f[i-1][1]\quad...)* \left( \begin{matrix} a_{00}&\quad a_{01}...\\ a_{10}&\quad a_{11}...\\ \cdots\\ a_{m-10}&\quad a_{m-11}...\\ \end{matrix} \right) \]
用矩阵快速幂优化即可
\[ (f[n][0]\quad f[n][1]\quad...)=(f[0][0]\quad f[0][1]\quad...)* \left( \begin{matrix} a_{00}&\quad a_{01}...\\ a_{10}&\quad a_{11}...\\ \cdots\\ a_{m-10}&\quad a_{m-11}...\\ \end{matrix} \right)^{n} \]
不过下面的代码,行和列是反着写的

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;

int a[25][25],f[25][25];
char s[25][25];
int n,m;
int mod;
int nxt[maxn];

void mul(int b[25][25],int c[25][25],int d[25][25])
{
    int tmp[25][25];
    for(int i=0;i<m;++i)
    {
        for(int j=0;j<m;++j)
        {
            tmp[i][j]=0;
            for(int k=0;k<m;++k)
            {
                tmp[i][j]=(tmp[i][j]+b[i][k]*c[k][j])%mod;
            }
        }
    }
    for(int i=0;i<m;++i)
    {
        for(int j=0;j<m;++j)
        {
            d[i][j]=tmp[i][j];
        }
    }
}
template<class T>void read(T &x)
{
    bool f=0;char ch=getchar();x=0;
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    if(f) x=-x;
}
int main()
{
    read(n);read(m);read(mod);
    scanf("%s",s+1);
    for(int i=2,j=0;i<=m;++i)
    {
        while(j&&s[i]!=s[j+1]) j=nxt[j];
        if(s[i]==s[j+1]) ++j;
        nxt[i]=j;
    }
    for(int i=0;i<m;++i)
    {
        for(int j=0;j<=9;++j)
        {
            int t=i;
            while(t&&s[t+1]!=j) t=nxt[t];
            if(s[t+1]-'0'==j) ++t;
            if(t<m) a[t][i]=(a[t][i]+1)%mod;//这里的行和列与我们定义的是相反的,这里是指从i转
        }                                  //移到t所有的方案数
    }
    for(int i=0;i<m;++i) f[i][i]=1;
    while(n)//矩阵快速幂加速
    {
        if(n&1) mul(f,a,f);
        mul(a,a,a);
        n>>=1;
    }
    int ans=0;
    for(int i=0;i<m;++i) ans=(ans+f[i][0])%mod;
    printf("%d\n",ans);
    return 0;
}

完了?

猜你喜欢

转载自www.cnblogs.com/iwillenter-top1/p/11828199.html