[ABC313F] Flip Machines

Problem Statement

There are $N$ cards numbered $1$ through $N$. Each face of a card has an integer written on it; card $i$ has $A_i$ on its front and $B_i$ on its back. Initially, all cards are face up.

There are $M$ machines numbered $1$ through $M$. Machine $j$ has two (not necessarily distinct) integers $X_j$ and $Y_j$ between $1$ and $N$. If you power up machine $j$, it flips card $X_j$ with the probability of $\frac{1}{2}$, and flips card $Y_j$ with the remaining probability of $\frac{1}{2}$. This probability is independent for each power-up.

Snuke will perform the following procedure.

  1. Choose a set $S$ consisting of integers from $1$ through $M$.
  2. For each element in $S$ in ascending order, power up the machine with that number.

Among Snuke's possible choices of $S$, find the maximum expected value of the sum of the integers written on the face-up sides of the cards after the procedure.

Constraints

  • $1\leq N \leq 40$
  • $1\leq M \leq 10^5$
  • $1\leq A_i,B_i \leq 10^4$
  • $1\leq X_j,Y_j \leq N$
  • All input values are integers.

\(N\le 40\),结合题目,复杂度应该是 \(2^{\frac n2}\) 相关的。

考虑如何求出期望。如果某一个数可能被选的话,那么他的期望就是 \(\frac {A_i+B_i}2\),否则是 \(A_i\)

看样例发现要特判 \(X_i=Y_i\),如果 \(A_{X_i}<B_{X_i}\),那么交换 \(A_{X_i}\)\(B_{X_i}\)

如果 \(A_i<B_i\),那么他被选后期望会减少,否则期望会增多。设有 \(c\)\(i\) 会变少。

如果 \(X_i\)\(Y_i\) 翻转后期望都会变少,肯定不翻。期望都会变多,肯定,肯定翻。现在就是要看 \(X_i\)\(Y_i\) 期望一个会变多,一个会变少的要不要翻。

肯定要对 \(c\) 大小分治。如果 \(c\le 20\),那么选了集合 \(s\) 中的所有会减少数,肯定会选所有可以选的会变多的数,直接枚举 \(s\) 统计即可。

如果 \(c\ge 20\),那么期望会增多的 \(i\) 不超过 20 个,用一个 dp 求出要得到集合 \(s\) 至少选多少减少的。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int M=45,N=1e5+5,S=1e7+5;;
int n,m,a[M],b[M],x[N],y[N],c,d,p[M],q[M],v[M][M];
LL to[S],s,f[M],ans;
LL calc(LL x,LL s)
{
	int ret=0;
	for(int i=0;i<d;i++)
	{
		if(x>>i&1)
			ret+=a[q[i]]+b[q[i]];
		else
			ret+=2*a[q[i]];
	}
	for(int i=0;i<c;i++)
	{
		if(s>>i&1)
			ret+=a[p[i]]+b[p[i]];
		else
			ret+=2*a[p[i]];
	}
	return ret;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
		scanf("%d%d",a+i,b+i);
	for(int i=0;i<m;i++)
	{
		scanf("%d%d",x+i,y+i);
		--x[i],--y[i];
		if(x[i]==y[i])
		{
			if(a[x[i]]<b[x[i]])
				swap(a[x[i]],b[x[i]]);
			--i,--m;
		}
		else
			v[x[i]][y[i]]=v[y[i]][x[i]]=1;
	}
	for(int i=0;i<n;i++)
		a[i]<b[i]? p[c++]=i:q[d++]=i;
	LL s=0;
	for(int i=0;i<c;i++)
		for(int j=i+1;j<c;j++)
			if(v[p[i]][p[j]])
				s|=1LL<<j|1LL<<i;
	for(int i=0;i<d;i++)
	{
		f[i]=s;
		for(int j=0;j<c;j++)
			if(v[q[i]][p[j]])
				f[i]|=1LL<<j;
	}
	if(d<=c)
	{
		for(int i=0;i<d;i++)
			to[1<<i]=f[i];
		ans=calc(0,s);
		for(int i=1;i<(1<<d);i++)
		{
			to[i]=to[i^(i&-i)]|to[i&-i];
			ans=max(ans,calc(i,to[i]));
		}
	}
	else
	{
		memset(to,-0x7f,sizeof(to));
		for(int i=0;i<(1<<c);i++)
			if((i|s)==s)
				to[i]=0;
		ans=calc(0,s);
		for(int i=1;i<(1<<c);i++)
		{
			if(!to[i])
				continue;
			for(int j=0;j<d;j++)
				to[i]=max(to[i],to[i^(i&f[j])]+b[q[j]]-a[q[j]]);
			ans=max(ans,calc(0,i)+to[i]);
		}
	}
	printf("%.6lf",ans/2.000);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值