1. 程式人生 > 其它 >YACS 2022年02月月賽 甲組 開關燈

YACS 2022年02月月賽 甲組 開關燈

https://iai.sh.cn/problem/589

晚自修摸魚 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;
}