PAROVI

link

暴力容斥。

虽然跑得比其它方法慢一些,但也不算太慢(最慢的点也就六十毫秒),个人觉得这种方法比较无脑,代码细节要少一些,简单来说就是好写。

首先合法的数对是可以暴力求得的,然后考虑补集转化,求出 Slavko 获胜的方案数就可以啦。但他获胜的方案怎么求呢?可以想到他的方案可以基于某种取法下 \(x\) 的取值进行分类,而 \(x\) 的值域很小,只有 \(20\) ,于是可以暴力地考虑枚举合法 \(x\) 的集合(严谨地说,枚举的是一定保证合法的 \(x\) 的集合,其它可能也有一些 \(x\) 的取值合法,但可以通过容斥去除重复方案)。如何计算方案呢?很简单,把不满足当前 \(x\) 集合的数对全部删掉,显然剩下的数对不论如何选择都可以满足枚举出的集合合法,所以假如剩下了 \(m\) 个数对,那么此时的方案数就是 \(2^m\)。然后就可以啦,剩下的问题就是简单容斥即可。复杂度是 \(O(2^NN^2)\) 但跑不满,由于常数比较小跑起来其实也算不上太慢(但和动规还是无法相提并论呜呜呜)。

代码,个人认为比较简洁易懂。

#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=25;
const int M=N*N;
const int mod=1e9;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

inline int gcd(int s1,int s2){
	return s2==0?s1:gcd(s2,s1%s2);
}

int a[M],b[M],cnt;
int m,p[M];

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);
	for(int i=1;i<m;i++){
		for(int j=i+1;j<=m;j++){
			if(gcd(i,j)==1){
				cnt++;a[cnt]=i;b[cnt]=j;
			}
		}
	}
	p[0]=1;
	for(int i=1;i<=cnt;i++)p[i]=p[i-1]*2%mod;
	int ans=p[cnt];
	for(int i=1;i<(1<<m);i++){
		if(i&1)continue;
		int num=0,cc=0,sum[N]={0};bool c[N]={0};
		for(int j=2;j<=m;j++)cc+=(c[j]=(i&(1<<j-1))!=0),sum[j]=sum[j-1]+c[j];
		for(int j=1;j<=cnt;j++){
			if(sum[b[j]]-sum[a[j]]==0)num++;
		}
		ans+=p[num]*((cc&1)?-1:1);ans%=mod;
	}
	printf("%lld\n",(ans%mod+mod)%mod);
	
	return 0;
}
posted @ 2022-07-23 14:36  Feyn618  阅读(10)  评论(0编辑  收藏  举报