游戏 - 博弈论 - 结论

题目大意:你有两个序列 { a n } { b m } \{a_n\}\{b_m\} {an}{bm},以及两个指针 c , d c,d c,d,初始 c = d = 1 c=d=1 c=d=1。有两个人,每次每个人可以选择修改c和d中的恰好一个,或者结束游戏,结果是 a c + b d a_c+b_d ac+bd。同一对(c,d)不能被访问100次。第一个人希望结果尽量小,第二个人希望结果尽量大,问最后结果是多少。
题解:首先等价于每一对(c,d)只能访问一次。然后二分答案x,看先手能否使得结果不大于x。这样一开始结果是大于x的,先手必须调整使其小于等于x,然后第二个人再让结果大于x……将 a c + b d ≤ x a_c+b_d\le x ac+bdx的(c,d)看做白点,否则黑点,相当与每次每人都要把棋子从当前点移动到颜色不同的没有被访问过的点。不能移动者输。
这是个经典结论,结论是某个白点先手必胜当且仅当其在一个最大匹配上。
因此只需要看删掉这个点是否最大匹配减小1。转化一下发现这等价于最大独立集不变。然后发现如果a和b都从小到大排序,那么一定存在一个最优解,存在一个分界点,选出来的白点都在分界点左上,黑点都在右下。接着发现枚举横坐标后,分界点纵坐标越大,独立集增量越小,所以可以二分;接着发现当横坐标变大的时候,最优的纵坐标不会变小,因此可以在线性时间内完成判定。最后如何判定扣去一个点的最大独立集ans2和原来最大独立集ans1是否一样,这个注意到如果ans2的分界点在扣去的点的左上,那么ans1一定大于ans2。因此也就是想知道是否存在一个分界点,取到最大独立集,并且右下方没有扣去的点即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=100010;
int a[N],b[N],R[N],U[N];
inline int check(int x,int n,int m,int p1,int p2)
{
	for(int i=1,j=m;i<=n;R[i++]=j)
		while(j&&a[i]+b[j]>x) j--;R[0]=m,R[n+1]=0;
	rep(i,0,n) rep(j,R[i+1]+1,R[i]) U[j]=i+1;
	lint s=0;rep(i,1,m) s+=n-U[i]+1;lint ans1=s,ans2=s-1;
	for(int i=0,j=0;i<=n;i++)
	{
		s+=min(R[i],j)-(m-max(R[i],j));
		while(j<m&&min(i,U[j+1]-1)>=n-max(U[j+1],i+1)+1)
			s+=min(i,U[j+1]-1)-(n-max(U[j+1],i+1)+1),j++;
		ans1=max(ans1,s),ans2=max(ans2,s-(p1>i&&p2>j));
	}
	return ans1==ans2;
}
int main()
{
	int n=inn(),m=inn();
	rep(i,1,n) a[i]=inn();int v1=a[1];
	rep(i,1,m) b[i]=inn();int v2=b[1];
	sort(a+1,a+n+1),sort(b+1,b+m+1);
	int p1=0;rep(i,1,n) if(a[i]==v1) p1=i;
	int p2=0;rep(i,1,m) if(b[i]==v2) p2=i;
	int L=a[1]+b[1],R=v1+v2-1;
	while(L<=R)
	{
		int mid=(L+R)>>1;
		if(check(mid,n,m,p1,p2)) R=mid-1;
		else L=mid+1;
	}
	return !printf("%d\n",L);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值