/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
/*容斥原理 给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm。
请你求出 1~n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=20;
int n,m;
int p[N];
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++) cin>>p[i];//读入m个质数
int res=0;//记录答案多少个
for(int i=1;i<1<<m;i++)//二进制枚举二进制数的每一位,用来看第几位有1,也就是有多少个集合
{
int t=1,cnt=0;//t该位的数,cnt用来记录二进制1的个数
for(int j=0;j<m;j++)//枚举位数
if(i>>j&1)//假如第j位是1
{
cnt++;//位数加一
if((ll)t*p[j]>n)//假如输入的质数大于n,则没答案
{
t=-1;
break;
}
t*=p[j];//位数上有1的数的乘积
}
if(t!=-1)//假如输入正确
{
if(cnt&1) res+=n/t;//假如是奇数个位数,也就是奇数个集合,则符号是加上
else res-=n/t;//假如是偶数个位数,也就是偶数个集合,则符号是减去上
}
}
cout<<res<<endl;//输出最后答案
return 0;
}
/*到达胜利之前无法回头*/
2 博弈论
有两种状态 先手必胜和先手必败
1 拿任意棋子
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//博弈论 拿棋子游戏 这里因为拿任意棋子看异或后的结果为不为0,假如是0则先手必败,反之先手必胜
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
int res=0;
scanf("%d",&n);
while(n--)
{
int x;
scanf("%d",&x);
res^=x;//异或每一个数
}
if(res) puts("Yes");//假如异或后答案不为0,则先手必胜
else puts("No");//反之先手必败
return 0;
}
/*到达胜利之前无法回头*/
2 拿规定的棋子,规定的棋子有多种拿法
/*
学acwing的算法基础课学来的,喜欢的话多多支持呀。
*/
//博弈论 拿棋子游戏 不是任意拿 规定拿数组中的多少个,有多种拿法
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10,M=1e2+10;
int n,m;//m是能拿的个数,n是石子的数
int s[M],f[N];//s存储的是可供选择的集合,f存储的是所有可能出现过的情况的sg值
int sg(int x)
{
if(f[x]!=-1) return f[x]; //记忆化搜索 看是否被算过,算过直接放回数值即可,降低时间复杂度
unordered_set<int> S;//用哈希表来存所有可以到的局面
for(int i=0;i<m;i++)//进行m次操作
{
int sum=s[i];//当前能拿石子的个数
if(x>=sum)//假如可以石子取
S.insert(sg(x-sum));//把新的状态加进来,先延伸到终点的sg值后,再从后往前排查出所有数的sg值
}
for(int i=0;;i++)//判断集合中不存在的最小自然数是多少
if(!S.count(i))//如果说当前这个数不存在的话
return f[x]=i;//说明当前这个数的sg的值就是它
}
int main()
{
cin>>m;
for(int i=0;i<m;i++) cin>>s[i];//可供选择拿的数
cin>>n;
memset(f,-1,sizeof f);//方便记忆化 初始化f均为-1,方便在sg函数中查看x是否被记录过
int res=0;
for(int i=0;i<n;i++)//n堆石子
{
int x;
scanf("%d",&x);
res^=sg(x);//观察异或值的变化,基本原理与Nim游戏相同
}
if(res) puts("Yes");//假如异或后答案不为0,则先手必胜
else puts("No");//反之先手必败
return 0;
}
/*到达胜利之前无法回头*/