YACS 2022年02月月赛 甲组 开关灯
晚自修摸鱼 15 min 想了出来。
考虑朴素覆盖,显然不行。
换种思路,考虑一个数被多少数覆盖到了,发现 m 很小,直接状压。
\(f[S]\) 表示仅以 S 状态的覆盖到的数的数量,即 \(f[S]\) 贡献的数不能贡献到 \(f[T],T\subseteq S\)。
发现可以容斥,即枚举超集,然后减去即可。
再发现转移顺序可能很奇怪,那么记搜。
注意 int128
。
#include <bits/stdc++.h>
#define int __int128
#define il inline
using namespace std;
il int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void pr(int x) {
if(x<0) {putchar('-');x=-x;}
if(x>9) pr(x/10);
putchar(x%10+'0');
}
const int N=(1<<16)+1;
int n,m,mS,f[N],a[20];
il int gcd(int x,int y) {
return !y?x:gcd(y,x%y);
}
il int lcm(int x,int y) {
return x/gcd(x,y)*y;
}
il int cal(int S) {
if(!S) return 0; int qwq=1;
// cout<<S<<endl;
for(int i=1;i<=m;i++) {
if((S>>(i-1))&1) {
qwq=lcm(qwq,a[i]);
// cout<<": "<<i<<" ";
}
}
// cout<<qwq<<" lcm\n";
return n/qwq;
}
il int dfs(int S) {
if(f[S]!=-1) return f[S];
int SS=mS-S,qwq=cal(S);
for(int T=SS;T;T=(T-1)&SS) {
int U=(T|S);
qwq-=dfs(U);
}
return f[S]=qwq;
}
il int cal1(int x) {
int cnt=0;
while(x) ++cnt,x-=(x&(-x));
return cnt;
}
signed main() {
memset(f,-1,sizeof(f));
n=rd(); m=rd(); mS=(1<<m)-1;
for(int i=1;i<=m;i++) a[i]=rd();
f[mS]=cal(mS); int ans=0;
for(int i=1;i<=mS;i++) if(f[i]==-1) dfs(i);
for(int i=1;i<=mS;i++) {
if(cal1(i)%2!=0) ans+=f[i];
}
pr(ans);
return 0;
}