bzoj1008: [HNOI2008]越狱(第100篇文章纪念)

题目描述

  监狱有连续编号为1…N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱

输入描述

  输入两个整数M、N.
  1<=M<= 108 ,1<=N<= 1012

输出描述

  可能越狱的状态数,模100003取余

样例输入

  2 3

样例输出

  6

题目思路

  (为了纪念第100篇文章,稍微写长一点)╮(╯▽╰)╭
  其实就是说有一个 n 位的m进制数,要你求出有多少个数存在两个或以上相同的相邻数字。
  似乎没什么思路,但我们很容易想到暴力,从全0开始枚举这个数字,枚举出的每个数字暴力检验它是不是符合要求。复杂度 O(nmm)
  考虑容斥,我们来统计有多少数字是每个相邻的数位都是不同的。那么就自然地来到了数位DP, f[i][j] 表示考虑只考虑前 i 位最后一位为j,不合法的方案数。那么

f[i][j]=f[i1][k] (k[1,m]kj)

  那么答案就等于 mnf[n][1...m]
  这样是 O(mn)
  考虑这样的一组数据, m=3n=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=m34×3=15 而通过暴力枚举发现答案确实是 15
  你发现每一行的数值都是一样的,于是可以前缀和优化, s[i]=f[i][1...m]
  那么 s[i]=m[s[i1]m(m1)]
  化简 s[i]=s[i1](m1)
  而 s[1]=m ,根据等比数列的知识写出通项 s[n]=m×(m1)n1
  那么 ans=mns[n]=mnm(m1)n1
  快速幂求解即可,复杂度 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值