生物 (裴蜀定理 容斥原理) [2020.5.2]

生物

题目描述

在一个无限长的一维空间中,存在着一个奇特的生物,它的身体上顺次有着 n + 1 个刻印,每个刻印可以用一个正整数来表示。已知它最后一个刻印的值为 m,而其它 n 个刻印的值均不超过 m,并且两个刻印的值可以相同。
这个生物每次可以选中它的任意一个刻印,并且按照这个刻印的值 k,选择向它所在位置的前或后闪烁 k 个单位。我们称可以使得这个生物能够通过若干次闪烁,到达一维空间任何一个位置的刻印序列为超刻印序列。
现在刻印序列显然一共有 $m^n$ 种,为了研究这个生物,请你求出其中超刻印序列的数目。

输入格式

仅一行两个整数,分别为 n, m。

输出格式

输出一行一个整数,表示超刻印序列的数目对 $10^9+7$ 取模的结果。

样例输入

2 4

样例输出

12

数据范围与约定

对于前 20%的数据,保证 n,m <= 5;
对于 100%的数据,保证 1<=n<=15,1<=m<=$10^8$



解题思路

首先转化一下复杂的题面
给定一个序列最后一项是m,之前有n项满足\(1<=n<=m\)。求有多少个序列的最大公因式为1?

令最大公因数为 d,则考虑用 \(m^n\) 减去 d≠1 的情况
注意到序列中一定有值 m,考虑令其质因子分别有 \(p_1, p_2, …, p_k\),故 d 不为 1 时一定为其中之一的倍数
则 d≠1 的情况数即求 { 满足 \(p_1|d\) 的序列 } ∪ { 满足 \(p_2|d\) 的序列 } ∪ … ∪ { 满足 \(p_k|d\) 的序列 } 此并集的大小

由于并集难求,而交集易求,使用容斥原理即可
例如对于求交集{ 满足 \(p_1|d\) 的序列 } ∩ { 满足 \(p_2|d\) 的序列 }大小
则让每个位置的数都是 \(p_1\ p_2\)的倍数即可,交集大小即\((m/p_1/p_2)^n\)

考虑时间复杂度,令 m 的质因子数目为 t,则容斥原理的复杂度为 \(\mathrm{O}(2^t)\)
而且一个数的质因子数目是很少的
对于具有 9 个质因子的数,最小也是\(2*3*5*7*11*13*17*19*23=223092870\)
所以容斥原理显然能够满足时限要求



Code:

Click for the code.
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;

int n, m, od[105];
int pri[10005], tag[10005], cnt;
int turn[10005], tot;
int len, f;
int ans = 0;

void get_pri() {//朴实无华的欧拉筛
	for (int i = 2; i <= 10000; i++) {
		if (!tag[i]) {
			pri[++cnt] = i;
			tag[i] = i;
		}
		for (int j = 1; j <= cnt && i * pri[j] <= 10000; j++) {
			tag[i * pri[j]] = pri[j];
			if (i % pri[j] == 0) break;
		}
	}
}

void init() {//分解质因数
	int k = m;
	for (int i = 1; i <= cnt; i++) {
		if (k % pri[i] == 0) {
			turn[++tot] = pri[i];
			while (k % pri[i] == 0) k /= pri[i];
		}
	}
	if (k != 1) turn[++tot] = k;
	return ;
}

int fast_pow(int base, int k) {//快速幂
	int res = 1;
	while (k) {
		if (k & 1) res = 1ll * res * base % mod;
		base = 1ll * base * base % mod;
		k >>= 1;
	}
	return res;
}

void dfs(int pos) {//容斥原理,写的很垃圾,可以更快,但是懒得改
	if (pos == len + 1) {
		int k = m;
		for (int i = 1; i <= len; i++) k /= turn[od[i]];
		ans = (ans + f * fast_pow(k, n)) % mod;
		return ;
	}
	for (int i = od[pos - 1] + 1; i <= tot; i++) {
		od[pos] = i;
		dfs(pos + 1);
	}
}

int main() {
//	freopen("creature.in", "r", stdin);
//	freopen("creature.out", "w", stdout);
	cin>>n>>m;
	get_pri();
	init();
	for (int i = 1; i <= tot; i++) {
		len = i; 
		f = (i & 1) ? 1 : -1; //容斥的系数
		dfs(1);
	}
	cout<<((fast_pow(m, n) - ans) % mod + mod) % mod<<endl;
}
posted @ 2022-05-07 23:34  Aiza  阅读(113)  评论(0编辑  收藏  举报