标签:题意 范围 考证 输入 cst pow while tmp 方案
有\(n\)个位置,\(m\)种数,每个位置填一个数,使得有相邻两位置的数相同,问有多少种方案。每种数的个数都是无限的。
明显的排列组合问题
我们现在先不考虑相邻位置的数相同的条件,则一共有
\[
m^n
\]
种方案,再减去每个相邻位置的数都不相同的方案总数。
怎么算呢?
第一个人有m种选择,第二个人因为要与第一个人不同,故有m-1种选择
第三个人因为要与第二个人不同,故有m-1(除去第二个人的那个数)
...
第n个人有m-1种选择,故共有
\[
m^n-m*(m-1)^{n-1}
\]
种方案
#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);
}
收获:补集转化,数学分析,快速幂
阿申准备报名参加\(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}
\]
不过下面的代码,行和列是反着写的
#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;
}
完了?
标签:题意 范围 考证 输入 cst pow while tmp 方案
原文地址:https://www.cnblogs.com/iwillenter-top1/p/11828199.html