Educational Codeforces Round 138 D. Counting Arrays(数论/计数原理)

题意:

对于一个数组a,可以删除a[i]当且仅当gcd(i,a[i])=1,删除后这个位置后面的数将向前平移。现在给定nm,问有多少个长度不超过n,元素不超过m的数组,存在不止一种的清空方式。

考虑长度固定为n时的答案。正难则反,可以求出总数量然后减去只有一种清空方式的数组个数。首先可以注意到第一个位置的数无论什么时候都是可以清空的,那么一种方式是每次都删掉第一个位置的数,这对于任何数组都是一种合法的方案,那么就需要保证,在任何时候,从第二个位置开始的数都不能被删除,即这个长度为n的数组一开始要满足:

对于第i个位置的数a[i]j[2,i],gcd(a[i],j)1(这是因为如果一直删第一个位置的数,要删i1次才能到a[i],在这之前a[i]的位置是i一直到2都有可能)

这也就是说,a[i]1i中所有质数乘积的倍数且不能大于m。设质数乘积的倍数是mul,那么合法的a[i]取值就有mmul个,设这个值是vali,用乘法原理就能得到长度为n的只有一种清空方式的数列个数为Πi=1nvali,因此也就能求出长度为1n的只有一种清空方式的数列个数了,用总数去减即可。

需要注意的是,因为m很大,所以特别需要注意取模以免进行乘法的时候就爆long long。

#include <bits/stdc++.h>
#define ll long long
#define int long long
#define N 1000005
#define mod 998244353
using namespace std;
bool prime[N];
ll fpow(ll a, ll b) {
	ll ans = 1;
	for(; b; b >>= 1) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}
void solve() {
	int n, m;
	cin >> n >> m;
	for(int i = 2; i <= N - 5; i++) {
		if(!prime[i]) {
			for(int j = 2 * i; j <= N - 5; j += i) {
				prime[j] = 1;
			}
		}
	}
	int mul = 1;
	int res = 1;
	int tans = 0;
	for(int i = 1; i <= n; i++) {
		if(!prime[i]) {
			mul = mul * i;
		}
		if(mul > m) break;
		res = (res * ((m / mul) % mod)) % mod;//(m / mul)后要先取模 防止爆ll
		tans = (tans + res);
	}
	int tot = 0;
	int tmp = 1;
	for(int i = 1; i <= n; i++) {
		tmp = tmp * (m % mod) % mod;//要先对m取模 防止爆ll
		tot = (tot + tmp);
	}
	cout << (tot - tans + mod) % mod;
}
signed main() {
	int T = 1;
	// cin >> T;
	while(T--) {
		solve();
	}
	return 0;
}
posted @   脂环  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2020-10-21 Codeforces Round #677 (Div. 3) A-E
点击右上角即可分享
微信分享提示
主题色彩