集合中的质数(容斥原理和dfs)

  • 注释:当然这道题也可以二进制枚举,只是不用 0 \small 0 0这个二进制。
  • 题面
  • 题意:见题面。
  • 解决思路
    容斥原理

    总结:容斥原理就是奇加偶减。
    有了公式就直接 d f s \small dfs dfs求解就可。
    变量的含义
    {   p o s : 遍 历 数 组 的 第 p o s 位   o p t : 容 斥 原 理 的 符 号 ( + 1 或 − 1 )   m u l : 第 p o s 位 之 前 所 选 的 数 的 乘 积 \small \begin{cases} \ pos:遍历数组的第pos位\\ \ opt:容斥原理的符号(+1或-1)\\ \ mul:第pos位之前所选的数的乘积\\ \end{cases}  pos:pos opt:(+11) mul:pos
    从左往右遍历每个数,对于这个数来说,可以取,也可以不取。
    不取:除了遍历的位置加一,其余不变。
    :遍历的位置加一,由容斥原理的奇加偶减的性质可以得到,多取一个数,它肯定变号,所以 o p t \small opt opt加负号,当前的数乘 u [ p o s ] \small u[pos] u[pos]
    会发现容斥原理至少取一个数,没有不取数的情况,所以当遍历到 p o s \small pos pos时,答案的贡献必须算上取 u [ p o s ] \small u[pos] u[pos]的情况,由于 ( m u l : 第 p o s 位 之 前 所 选 的 数 的 乘 积 ) \small (mul:第pos位之前所选的数的乘积) (mul:pos),对答案的贡献为: m \small m m m u l ∗ u [ p o s ] \small mul*u[pos] mulu[pos]下取整。
    排序剪枝操作:发现题目,当数的乘积大于 m \small m m时,就对答案没贡献了,所以当遍历到 p o s \small pos pos已经越界时,后面的数就可以不用遍历了。
  • AC代码
//优化
#pragma GCC optimize(2)
//C
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
//C++
#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<istream>
#include<iomanip>
#include<climits>
#include<float.h>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
//宏定义
#define N 1010
#define DoIdo main
//#define scanf scanf_s
#define it set<ll>::iterator
#define TT template<class T>
#define cint const int 
//定义+命名空间
typedef long long ll;
typedef unsigned long long ull;
const int mod = 10007;
const ll INF = 1e18;
const int maxn = 1e6 + 10;
using namespace std;
//全局变量
ll n, m;
ll u[25];
ll ans = 0;
//函数区
ll max(ll a, ll b) { return a > b ? a : b; }
ll min(ll a, ll b) { return a < b ? a : b; }
ll loop(ll n, ll p) { return (n % p + p) % p; }
void dfs(ll pos, ll opt, ll mul) {
	//如果遍历的数超过输入的数,则返回
	if (pos == n + 1) return;
	//这就是u[pos] * mul <= m的变形
	//这样可以预防long long溢出
	if (u[pos] <= m / mul) {
		//加一下对答案的贡献
		ans += opt * m / (mul * u[pos]);
		//不取的情况
		dfs(pos + 1, opt, mul);
		//取的情况
		dfs(pos + 1, -opt, mul * u[pos]);
	}
	else return;
}
//主函数
int DoIdo() {

	ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);

	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		cin >> u[i];
	}

	sort(u + 1, u + n + 1);
	dfs(1, 1, 1);

	cout << ans << endl;
	return 0;
}
//分割线---------------------------------QWQ
/*



*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值