PAROVI
暴力容斥。
虽然跑得比其它方法慢一些,但也不算太慢(最慢的点也就六十毫秒),个人觉得这种方法比较无脑,代码细节要少一些,简单来说就是好写。
首先合法的数对是可以暴力求得的,然后考虑补集转化,求出 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;
}
一如既往,万事胜意