codeforces 794E Choosing Carrot

codeforces 794E Choosing Carrot

题目传送门

题意

给出一个长度为n的序列,AB开始玩游戏,每次可以从序列的两端删除一个数,直到删除到最后一个数,A想要使最后一个数尽量的大,而B想要使最后一个数尽量的小,A先手,问最后剩下的数是多少。然后问如果A先进行k(k=1...n1)次操作,那么结果是多少。

题解

感觉这个E题有点水首先我们考虑不进行先手操作,那么答案是多少。我们根据题目可以发现这题一定是和n的奇偶有关系的,因为这关系到A是否会多取一次。然后由于这是个博弈,所以我们需要考虑什么状态下某个人是优的或者必胜的。然后我们仔细思考可以知道在n为偶数的情况下,最后剩下的一个数一定是a[n/2]a[n/2+1]这之中较大的一个。因为由于B需要阻挠A删去那些小的数最后得到大的数,并且B是后手,所以它每次取一定都会取上次A取的那端的另一端,然后最后就会只剩下两个数,那么A就只能选择较大的那个了。
同样,当n为奇数的时候,A先手取了一个,那么就会转换成n为偶数并且B先手的情况了。由于最开始的那一步A有两种取法,那么最后剩下的两个数会是a[(n+1)/21]a[(n+1)/2]a[(n+1)/2]a[(n+1)/2+1]。由于这样最后一步会是B进行选择,所以B一定会选择较小的那个,那么最后的两种情况一定是min(a[(n+1)/21],a[(n+1)/2])min(a[(n+1)/2],a[(n+1)/2+1])。然而A先手,就一定会选择这两个之中较优的情况,所以最后答案一定是max(min(a[(n+1)/21],a[(n+1)/2]),min(a[(n+1)/2],a[(n+1)/2+1]))
写了这么多终于写完初始情况了23333。接下来我们考虑进行先手操作之后,答案会进行怎样的变化。由于我们考虑初始情况的时候是分奇偶讨论的,而进行先手操作完之后的状况实际上是与初始状况一样的,所以我们在这里仍然是要分奇偶讨论。观察一下之前的结论,我们先把未经过先手操作的答案列出来:

half={(n+1)/2n&1=1n/2n&1=0

res={max(min(a[half1],a[half]),min(a[half],a[half+1]))n&1=1max(a[half],a[half+1])n&1=0

我们可以发现,答案总是与这个中心点half有关,而进行先手操作的意义就是在于使得这个half值能够改变。我们记half的活动区间[l,r]表示[l,r]区间的每个数都是可以成为答案的half的。而先手操作的次数每增加2,就会使这个half[l,r]向左向右拓宽1个长度。这样我们只需要枚举先手操作的次数k,然后就将新拓宽区间的答案与k2的答案取max,就是当前的答案。当然,这也是要分奇偶的。我们记ans1ans2分别表示进行k次先手操作之后,nk分别为奇数和偶数的答案。为了方便计算,我们记res[i][0]=max(min(a[i1],a[i]),min(a[i],a[i+1]))res[i][2]=max(a[i],a[i+1])res[i][2]=max(a[i1],a[i])
L1,R1,L2,R2分别为nk为奇数和偶数时的活动区间。
然后我们就可以进行转移了,若nk为奇数,那么这样更新:ans1=max(ans1,max(res[L1][0],res[R1++][0]))
nk为偶数,则这样转移:
ans2=max(ans2,max(res[L2][2],res[R2++][3]))
最后注意特判一下k=n1k=n2的情况,这两种情况A都可以直接取完n1个数,所以直接输出a[]中最大的那个就行了。

如果还不懂的话可以看题解

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
#define PAUSE printf("Press Enter key to continue..."); fgetc(stdin);
const int N=3e5+500;
int n;
int a[N],res[N][3];
int ans1,ans2,L1,R1,L2,R2,Mx;
/*==================Define Area================*/
int main() {
	read(n);
	for(int i=1;i<=n;i++) {
		read(a[i]);Mx=max(Mx,a[i]);
	}
	for(int i=1;i<=n;i++) {
		if(i==1) res[i][0]=res[i][2]=a[i],res[i][5]=max(a[i],a[i+1]);
		else if(i==n) res[i][0]=res[i][6]=a[i],res[i][2]=max(a[i],a[i-1]);
		else res[i][0]=max(min(a[i],a[i-1]),min(a[i],a[i+1])),res[i][7]=max(a[i],a[i+1]),res[i][2]=max(a[i],a[i-1]);
	}
	if(n<=2) {
		for(int i=1;i<=n;i++) printf("%d\n",Mx);
		return 0;
	}
	if(n==3) {
		printf("%d %d %d\n",res[2][0],Mx,Mx);
		return 0;
	}
	if(n&1) {
		ans1=res[(n+1)/2][0];
		ans2=max(res[(n+1)/2][8],res[(n+1)/2][2]);
		L1=L2=(n+1)/2-1;R1=R2=(n+1)/2+1;
		printf("%d %d ",ans1,ans2);
	}
	else {
		ans2=res[n/2][9];
		ans1=max(res[n/2][0],res[n/2+1][0]);
		L2=n/2;R2=n/2+1;L1=n/2-1;R1=n/2+2;
		printf("%d %d ",ans2,ans1);
	}
	// printf("%d %d %d %d\n",L1,R1,L2,R2);
	for(int i=2;i<n-2;i++) {
		if((n-i)&1) {
			ans1=max(ans1,max(res[L1--][0],res[R1++][0]));
			printf("%d ",ans1);
		}
		else {
			ans2=max(ans2,max(res[L2--][2],res[R2++][10]));
			printf("%d ",ans2);
		}
	} 
	printf("%d %d\n",Mx,Mx);
	return 0;
}
posted @   Apocrypha  阅读(351)  评论(0编辑  收藏  举报
编辑推荐:
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
阅读排行:
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 使用 Dify + LLM 构建精确任务处理应用
点击右上角即可分享
微信分享提示