CF1875B Jellyfish and Game

发布时间 2023-10-01 10:23:04作者: One_JuRuo

思路

题意大概是两人都有一组数,奇数轮,第一个人可以选择和第二个人交换一个数字也可以不换,偶数轮,第二个人可以选择和第一个人交换一个数字也可以不换。

首先可以猜测,我们每次都应该选择交换对方的最大值和自己的最小值,如果自己的最小值都比对方大的话就不交换。应该比较好想,这里感性证明一下。

如果用的不是自己的最小值去交换,那么总和会变小,对方也不会再交换你的最小值,所以这一步是亏的。

如果交换的不是对方的最大值,如果这个值本身就是你的最大值,那么对方又换回来,没有影响,如果不是,对方交换回去,你就损失了你的最大值,比你交换对方的最大值会亏损一些总和。

如果不需要交换而强制交换,那么会让自己的总和变小,对方再交换一次,又亏损一定的答案,不值得。

那么知道这个性质,我们可以推测,大部分的游戏都一个讨论,两个人把两个数交换过去交换过来,所以我们不需要模拟,可以直接跳过许多无用的游戏。

因为 \(k\) 的奇偶决定着谁结束游戏,也就是最后是谁交换的,这个信息是很重要的,所以我们可以考虑分成奇偶两种情况分别考虑。

首先,当 \(k\) 是奇数时,由第一个人结束游戏,所以如果第二个人的最大值比第一个人的最小值还要小的话,第一个人第一轮不交换,后续第二个人交换啥,第一个人就交换回来,所以答案不变。否则的话,第一个人会交换自己的最小值和对方的最大值,然后两人开始互相交换,最后第一个人交换成功,所以答案是原来的总和加上对方的最大值和自己的最小值。

然后,当 \(k\) 是偶数时,由第二个人结束游戏,和上面一种情况比较相似,如果第二个人的最大值比第一个人的最小值还要小,那么第一轮,第一个人不会交换,那么后续第二个人就可以成功交换,所以答案是总和减去第一个人的最大值加上第二个人的最小值。否则,答案不变。

想清楚了后,发现很简单,只需要在输入的时候记录两个人的最大最小值即可。

快速地写完,发现 WA 了。

思考了一下,发现当对方的最大值交换过来不是自己的的最大值时,就不是互相交换两个数,答案的变化也没有这么简单。

所以我们可以考虑先计算交换两轮的结果,第一轮可以保证最大值在第一个人身上,第二轮可以保证最大值在第二个人身上,并且最小值在第一个人身上,这样每次都必定交换最小值和最大值。

证明比较简单,第一轮如果最大值在自己身上,无论交不交换,最大值都还是自己的,如果不在自己身上,则必然交换,最大值就是第一个人的了。第二轮,第二个人必定交换,最大值就跑到第二个人身上了,第二个人会把自己的最小值给第一个人,所以第一个人同时有了自己和第二个人的最小值,所以最小值一定在第一个人身上。

所以我们可以先模拟两次过程,再上面的结论即可。

AC code

#include<bits/stdc++.h>
using namespace std;
long long T,n,m,k,sum1,sum2,minn,maxn,t,mi,ma;
multiset<long long>sa,sb;
int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld%lld",&n,&m,&k),sum1=sum2=0,sa.clear(),sb.clear();
		for(int i=1;i<=n;++i) scanf("%lld",&t),sum1+=t,sa.insert(t);//分别用set存下来
		for(int i=1;i<=m;++i) scanf("%lld",&t),sum2+=t,sb.insert(t);
		for(int i=1;i<=2&&i<=k;++i)//模拟2次,注意k<=2的情况
		{
			if(i%2)
			{
				auto a=sa.begin(),b=sb.end();--b;
				if(*b-*a>0) sum1+=*b-*a,sum2+=*a-*b,sa.insert(*b),sb.insert(*a),sa.erase(a),sb.erase(b);
			}
			else
			{
				auto a=sa.end(),b=sb.begin();--a;
				if(*a-*b>0) sum1+=*b-*a,sum2+=*a-*b,sa.insert(*b),sb.insert(*a),sa.erase(a),sb.erase(b);
			}
		}
		k-=2;
		auto mina=sa.begin(),maxa=sa.end(),minb=sb.begin(),maxb=sb.end();--maxa,--maxb;//用set直接找到双方的最大最小
		if(k<=0) printf("%lld\n",sum1);
		else if(k%2) printf("%lld\n",sum1+max(*maxb-*mina,0ll));
		else printf("%lld\n",(*maxb<*mina)?sum1-max(*maxa-*minb,0ll):sum1);//上面的结论
	}
	return 0;
}