题目大意
求不大于 m 的、 质因数集与给定有n个元素的质数集有交集的自然数之和。
数据范围
1 2 3 n*m<=10^7
4 5 n<=2,m<=10^9
6 7 n<=20,m<=10^8
8 9 10 n<=20,m<=10^9
前三个点
n可能会很大。暴力枚举从1到m的每一个数看看是否满足条件即可。
后七个点
m内因数中包含质数p的数分别为p*1,p*2,p*3...p*m/i。用等差数列的知识可以得到该数列的和,记为f(i)。根据容斥原理,结果为sum(f(p[i]))-sum(f(p[i]*p[j]))+sum(f(p[i]*p[j]*p[k]))-...。
n个元素的集合的子集数量为2^n,而后七个点n<=20,符合要求。
注意事项
- 每次想要对P取模时,不能直接x%P,而应当为(x%P+P)%P,因为运算中变量有可能暂时是负的。
- 作为判断终止条件的当前乘积prod不能取模。
#include <cstdio> #include <cstring> #include <algorithm> #include <cassert> using namespace std; #define ll long long const int P = 376544743, INV2 = 188272372; const int MAX_N = 100000; ll Primes[MAX_N]; ll N, M, Ans; ll Mod(ll x, ll p) { return (x % p + p) % p; } ll Mult(ll a, ll b) { ll ans = 0; while (b) { if (b & 1) ans = Mod(ans + a, P); a = Mod(a + a, P); b >>= 1; } return ans; } void Dfs(int cnt, int p, ll prod) { assert(prod <= M); if (cnt > 0) { ll n = M / prod; ll delta = Mult(Mult(n, n + 1), INV2); delta = Mult(delta, prod); if (cnt & 1) Ans = Mod(Ans + delta, P); else Ans = Mod(Ans - delta, P); } if (p == N) return; ll NextProd; for (int i = p + 1; i <= N && (NextProd=prod*Primes[i])<=M; i++) Dfs(cnt + 1, i, NextProd); } ll way1() { ll ans = 0; for (ll i = 1; i <= M; i++) { for (ll j = 1; j <= N && Primes[j] <= i; j++) { if (i % Primes[j] == 0) { ans = (ans + i) % 376544743; break; } } } return ans; } int main() { scanf("%lld%lld", &N, &M); for (int i = 1; i <= N; i++) scanf("%lld", Primes + i); sort(Primes + 1, Primes + N + 1); if (N*M <= 10000000) printf("%lld ", way1()); else { Ans = 0; Dfs(0, 0, 1); printf("%lld ", Ans); //printf("%lld ", Ans%P); } return 0; }