luogu2429 制杖题

题目大意

求不大于 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\n", way1());
	else
	{
		Ans = 0;
		Dfs(0, 0, 1);
		printf("%lld\n", Ans);
		//printf("%lld\n", Ans%P);
	}
	return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/headboy2002/p/8935006.html