题目描述
监狱有连续编号为1…N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱
输入描述
输入两个整数M、N.
1<=M<=
108
,1<=N<=
1012
输出描述
可能越狱的状态数,模100003取余
样例输入
2 3
样例输出
6
题目思路
(为了纪念第100篇文章,稍微写长一点)╮(╯▽╰)╭
其实就是说有一个
n
位的
似乎没什么思路,但我们很容易想到暴力,从全0开始枚举这个数字,枚举出的每个数字暴力检验它是不是符合要求。复杂度
O(nmm)
。
考虑容斥,我们来统计有多少数字是每个相邻的数位都是不同的。那么就自然地来到了数位DP,
f[i][j]
表示考虑只考虑前
i
位最后一位为
f[i][j]=∑f[i−1][k] (k∈[1,m]且k≠j)
那么答案就等于 mn−∑f[n][1...m]
这样是 O(mn) 的
考虑这样的一组数据, m=3,n=3
我们可以写出状态:
f[1][0]=1 f[1][1]=1 f[1][2]=1f[2][0]=2 f[2][1]=2 f[2][2]=2f[3][0]=4 f[3][1]=4 f[3][2]=4
ans=m3−4×3=15 而通过暴力枚举发现答案确实是 15
你发现每一行的数值都是一样的,于是可以前缀和优化, s[i]=∑f[i][1...m]
那么 s[i]=m[s[i−1]m(m−1)]
化简 s[i]=s[i−1](m−1)
而 s[1]=m ,根据等比数列的知识写出通项 s[n]=m×(m−1)n−1
那么 ans=mn−s[n]=mn−m(m−1)n−1
快速幂求解即可,复杂度 O(log2n)
一开始推出来的时候有点不相信,但居然1A了。。。连我这样的辣鸡都能想出来只能说明它很水
代码
#include <cstdio>
#include <algorithm>
#define p 100003
#define ll long long
using namespace std;
ll N, M;
ll pow(ll a, ll b)
{
ll ans=1, t=a;
for(;b;b>>=1,t=t*t%p)if(b&1)ans=ans*t%p;
return ans;
}
int main()
{
ll ans;
scanf("%lld%lld",&M,&N);
ans=pow(M,N)-M*pow(M-1,N-1);
ans=(ans%p+p)%p;
printf("%lld\n",ans);
return 0;
}