【每日一题】洛谷 P1621 (容斥原理 + 快速幂)

想她一次就背十个单词,当我英语过六级后,我就去告诉她,我很在意她
一天一道数论题,当我可以秒杀数论题的时候,就开始做 DP


今日份快乐:洛谷 P1621(容斥原理 + 快速幂) 传送门
明天份快乐:洛谷 P1962(矩阵快速幂) 传送门


题目大意

监狱有 n 个房间,每个房间关押一个犯人,有 m 种宗教,每个犯人会信仰其中一种。如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱。

分析

如果数据规模小一点的话,搜索是可以做的。but,看看迷人的数据范围,就知道,肯定有O(1)的写法,又要推公式了。
n 个房间,m 种信仰,那么所以的排列组合数 sum = mn.
如果我们考虑相邻监狱有共同信仰的话,会发现情况有很多,很不容易计算,这时候,数学老师对我们的教导出现在了耳旁,正难则反
我们考虑,相邻房间全部都没有共同信仰的情况tot,则最后的答案 res = sum - tot .
我们取最左边的房间为第一个房间,第一个房间有 m 信仰可以选择,而和它相邻的第二个房间就有 (m - 1) 种选择。我们假现在第一个房间信仰 x ,第二个房间信仰 y,对于第三个房间,他可以选择的信仰中没有 y ,但可以有 x,第一个房间和第三个放假没挨着,所以,第三个房间有 (m - 1) 种选择。同理,往后的房间都有 (m - 1) 种选择。故而,相邻房间全部都没有共同信仰的数量 tot = m * (m - 1) ^(n - 1)^。即, res = mn - m * (m - 1) ^(n - 1)^。
这种正难则反的思维就叫做:容斥原理

在计数时,必须注意没有重复,没有遗漏。为了使重叠部分不被重复计算,人们研究出一种新的计数方法,这种方法的基本思想是:先不考虑重叠的情况,把包含于某内容中的所有对象的数目先计算出来,然后再把计数时重复计算的数目排斥出去,使得计算的结果既无遗漏又无重复,这种计数的方法称为容斥原理。
                                                                                              --来自百度百科

现在,O(1) 的公式有了,但是可以发现,计算 sum 和 tot 的时候有工程庞大的幂运算,这时候,就要用到快速幂运算
举个栗子来说明原理:

eg: a = 5, b = 13(10) = 1101(2) = 1 * 23 + 1* 22 + 0 * 21 + 1 * 2 0
ab = 5 13
   = 5 8+0+2+1
   = 58 * 50 * 52 * 51
如果我们用循环直接算 513需要算 13 次,也就是O(n)的时间复杂度
而,如果我们只计算 51 ,52,54 和 58,就可以直接得到 5 13,这样的时间复杂度就是O(log2n)

代码实现有很多种方式,我使用的是位运算

代码

	
#include <bits/stdc++.h>
using namespace std;
const ll mod = 100003;

ll quick_pow(ll a, ll b){     //快速幂运算 a^b,没太大必要看懂,记个模板就OK
	ll res = 1;
	while(b){
		if(b & 1) res *= a, res %= mod;
		a *= a;
		a %= mod;
  		b >>= 1;
  	}
  	return res;
}

int main() {

	ios::sync_with_stdio(false);
	
	ll n, m;
 	cin >> m >> n;
	
	ll sum_1 = quick_pow(m, n);
 	ll sum_2 = (quick_pow(m-1, n-1) % mod * m % mod) % mod;
 	ll res = (sum_1 - sum_2 + mod) % mod;       // 避免出现负数

	cout << res << endl;
    	return 0; 
}
	

坚持的时候很狼狈,等成功以后,丑的还是丑的🤭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值