2019.7.10 校内测试题 越狱
题目
越狱(prison.cpp,1s,512MB)
【问题描述】:
监狱有连续编号为 1..n 的 n 个房间,每个房间关押一个犯人。有 m 种宗教,
每个犯人可能信仰其中一种。如果相邻房间的犯人信仰的宗教相同,就可能发生
越狱。求有多少种状态可能发生越狱。
【输入文件】:
输入两个整数 m 和 n,1≤m≤108,1≤n≤1012。
【输出文件】:
可能越狱的状态数,模 100003 取余。
【输入输出样例】:
prison.in
2 3
prison.out
6
【数据规模】:
6 种状态为:(000)(001)(011)(100)(110)(111)
考试得分: 10
主要算法 : 同余(快速幂)
应试策略:
- 首先想到DP,但是条件不允许呀,所以考虑一下的,DP不行,数论题就决定是它了
- 总状态数位m^n,考虑不发生越狱的情况第一个有m种,第二个就有m-1种,那么第x个(x∈[2,n])都为m-1种,那么总数为m*(m-1)^(n-1)种,那么符合条件的就是m^n-m*(m-1)^(n-1)
- 那么最后输出的为pow(m,n,MOD)-m*pow(m-1,n-1,MOD)%MOD+MOD,需要考虑long long
代码
#include<stdio.h> #include<stdlib.h> #define LL long long #define FORa(i,s,e) for(LL i=s;i<=e;i++) #define FORs(i,s,e) for(LL i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),stdin)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; static char buf[100000],*pa=buf,*pb=buf; inline LL read(); const LL MOD=100003; LL m,n; LL Quickpow(LL a,LL b,LL c) { LL ret=1; while(b) { if(b&1) ret=ret*a%c; a=a*a%c,b>>=1; } return ret%c; } int main() { File("prison"); m=read(),n=read(); LL p=Quickpow(m,n,MOD)-(Quickpow(m-1,n-1,MOD)*m%MOD); printf("%lld",p%MOD); return 0; } inline LL read() { register LL f(1),x(0);register char c(gc); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return f*x; }
非完美算法:
照搬应试策略
正解:
因为取模运算的特殊性 ,所以Quickpow(m,n,MOD)-(Quickpow(m-1,n-1,MOD)*m%MOD)可能小于0,所有要先加上MOD再取模MOD,运用同余的同加性
总结:
- 取模运算之间相减注意符号,运用同余的同加性