专栏文章
题解:P13309 演剧
P13309题解参与者 4已保存评论 7
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 7 条
- 当前快照
- 1 份
- 快照标识符
- @mioux6j2
- 此快照首次捕获于
- 2025/12/03 01:33 3 个月前
- 此快照最后确认于
- 2025/12/03 01:33 3 个月前
思路
怎么最高赞是个二分啊,那我来写一手。也许画个图会清楚些?
我们先讨论 ,当最后剩下一个 的时候先手赢,剩下 时先手输。每次分割完之后交换先后手,同时根据题意原先的 变成 , 变成 (因为先后手目标相反,为了保证对称性做出如上变换)。
我们根据博弈的状态开始讨论:
- 终止状态:剩下一个 ,胜态;剩下一个 ,败态。
- 剩下若干数字,总和为正:可能可以分成两部分总和为正,此时对手的接续状态是总和为负;可能可以分成一部分总和为正,一部分总和为零,对手的接续状态是总和为负或总和为零。
- 剩下若干数字,总和为负:同上,对手总能合理选择使得接续状态是总和为正。
这个时候发现总和为正有胜态的雏形,总和为负有负态的雏形,但是还不够,继续讨论总和为零:
- 拟边界:总和为零且不能分割成两部分使得两部分总和均为零,此时先手划分出来的一定是一正一负,后手选择保留负段,故接续状态为和为正,所以这个边界暂定为败态;
- 总和为零,且如果分成尽可能多的和为 的段可以分出 段, 为偶数,则下一步可以分成两个 为奇数,或两个 为偶数。
- 为奇数,则下一步一定分为一个奇数 ,一个偶数 。
画个类似于自动机的状态图( 表示总和, 表示能分成 的极小段的个数 的奇偶性):

刚刚都是初步的推导,是为了确定我们要把什么作为胜态、什么作为负态去讨论,有了这个图我们就可以进行严谨的证明了:
严谨的证明
- 时,可能可以分成两段 使得对手进入 的状态;如果不能,那么一定可以分成一段 和一段 ,使对手要么进入 状态,要么进入 状态;
- 时,无论怎么分都必有一段 ,对手一定可以进入 状态;
- 时,要么分成一段 ,一段 ,对手可以进入 状态;要么分成一段 ,一段 ,对手可以进入 状态;
- 时,一定可以分成两段 ,使对手进入 状态。
- 终止胜态是 的子集,终止负态是 的子集,指向胜态的是负态,指向的全是负态的是胜态,归纳可得 和 是胜态, 和 是负态。
综上,我们得出了 时的结论。这个时候已经有了二分答案的做法,大于等于答案的为 ,小于答案的为 ,计算 和 就可以得到结果。
但是没有必要。可以发现当 时,答案为 的中位数满足 ,大于 的中位数则 ,所以此时答案就是 的中位数; 时,取较小的中位数则有 ,取较大的中位数则 ,此时若 为偶数,则答案为较大的中位数;否则答案只能取较小的中位数。
取中位数可以做到线性,时间复杂度 。下面代码的实现基于排序,时间复杂度 。
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N = 200000;
int T,n,a[N],b[N],c[N],k,s;
int main(){
cin.tie(0)->sync_with_stdio(0);
cin >> T;
while(T --){
cin >> n;
for(int i = 1;i <= n;i ++) cin >> a[i],b[i] = a[i];
sort(b + 1,b + n + 1);
if((n & 1) || b[n / 2] == b[n / 2 + 1]) printf("%d\n",b[n / 2 + 1]);
else{
for(int i = 1;i <= n;i ++){
if(a[i] > b[n / 2]) c[i] = 1;
else c[i] = -1;
}
k = s = 0;
for(int i = 1;i <= n;i ++){
s += c[i];
if(!s) k ++;
}
printf("%d\n",b[n / 2 + 1 - (k & 1)]);
}
}
return 0;
}
相关推荐
评论
共 7 条评论,欢迎与作者交流。
正在加载评论...