题解:P9316 [EGOI 2021] Double Move / 二选一游戏
二元操作关系,我们不妨直接考虑建图。容易发现若把每次操作的
令连通块点集为
对于前
对于后
状态数等价于选出可重集
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=40;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,k,X[N],Y[N],fa[N],siz[N],a[N];
long long sum=1;
inline int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
struct fac{
long long a,b;
inline bool operator<(const fac c)const{
return a*c.b<b*c.a;
}
inline fac operator*(fac c){
fac g={a*c.a,b*c.b};
long long now=__gcd(g.a,g.b);
assert(now);
return{g.a/now,g.b/now};
}
};
inline fac operator-(long long x,fac a){
fac g={x*a.b-a.a,a.b};
long long now=__gcd(g.a,g.b);
return{g.a/now,g.b/now};
}
map<vector<int>,fac>f;
inline fac dfs(int pos,vector<int>v){
// cerr<<pos<<' '<<v[1]<<' '<<v[2]<<' '<<v[3]<<endl;
if(pos==n+1)return {0,1};
if(f.count(v))return f[v];
f[v].a=-1,f[v].b=1;
vector<int>t=v;
for(int i = 1;i<=n;++i){
if(!v[i])continue;
t[i]--;
f[v]=max(f[v],(fac){2,i}*((1ll<<n+1-pos)-dfs(pos+1,t)));
for(int j = i;j<=n;j++){
if(!t[j])continue;
t[j]--;t[i+j]++;
f[v]=max(f[v],(fac){i+j,i*j}*((1ll<<n+1-pos)-dfs(pos+1,t)));
t[j]++;t[i+j]--;
}
t[i]++;
}
// cerr<<pos<<' '<<v[0]<<' '<<v[1]<<' '<<f[v].a<<' '<<f[v].b<<endl;
return f[v];
}
int main(){
n=read(),k=read();
long long ans = 0;
for(int i = 1;i<=n;++i)fa[i]=i,siz[i]=1;
vector<int>v(n+1);
for(int i = 1;i<=k;i++){
X[i]=find(read()),Y[i]=find(read());
if(X[i]==Y[i]){
a[X[i]]++;
if(a[X[i]]>siz[X[i]]){
if(i%2==0)ans+=sum*(1ll<<n+1-i+1);
printf("%lld %lld\n",ans,(1ll<<n+1)-ans);return 0;
}
if(i%2==0)ans+=(1ll<<n+1-i+1)*sum/siz[X[i]]*(siz[X[i]]-1);
sum/=siz[X[i]];sum*=2;
}
else{
if(a[X[i]]==siz[X[i]]&&a[Y[i]]==siz[Y[i]]){
if(i%2==0)ans+=sum*(1ll<<n+1-i+1);
printf("%lld %lld\n",ans,(1ll<<n+1)-ans);return 0;
}
if(siz[X[i]]>a[X[i]]){
if(i%2==0)ans+=(1ll<<n+1-i)*(sum/siz[X[i]]*(siz[X[i]]-1));
}
else{
if(i%2==0)ans+=(1ll<<n+1-i)*sum;
}
if(siz[Y[i]]>a[Y[i]]){
if(i%2==0)ans+=(1ll<<n+1-i)*(sum/siz[Y[i]]*(siz[Y[i]]-1));
}
else{
if(i%2==0)ans+=(1ll<<n+1-i)*sum;
}
if(siz[X[i]]>a[X[i]])sum/=siz[X[i]];
else sum/=2;
if(siz[Y[i]]>a[Y[i]])sum/=siz[Y[i]];
else sum/=2;
fa[X[i]]=Y[i],siz[Y[i]]+=siz[X[i]];a[Y[i]]+=a[X[i]]+1;
if(siz[Y[i]]>a[Y[i]])sum*=siz[Y[i]];
else sum*=2;
}
}
// cout<<ans<<endl;
for(int i = 1;i<=n;i++)if(fa[i]==i&&siz[i]>a[i])v[siz[i]]++;
auto[x,y]=dfs(k+1,v);
if((k+1)&1)ans+=sum*x/y;
else ans+=sum*(1ll<<n+1-k) - sum*x/y;
printf("%lld %lld\n",ans,(1ll<<n+1)-ans);
return 0;
}