https://www.zybuluo.com/ysner/note/1300791
题面
贝西和她的朋友艾尔西正在玩这个简单的纸牌游戏。游戏有(2N)张牌,牌上的数字是(1)到(2N)。把这些牌分成两份,贝西有(N)张,艾尔西有另外(N)张。接下来她们进行(N)轮出牌,每次各出一张牌。一开始,谁出的牌上的数字大,谁就获得这一轮的胜利。贝西有一个特殊权利,她可以在任意一个时刻把原本数字大的获胜的规则改成数字小的获胜,这个改变将会一直持续到游戏结束。特别的,贝西可以从第一轮开始就使用小牌获胜的规则,也可以直到最后一轮都还不使用大牌获胜的规则。
现在,贝西已经知道了艾尔西出牌的顺序,她想知道她最多能够赢多少轮。
解析
显然前面每次选择比对方大却又尽可能小的牌,后面选择比对方小却又尽可能大的牌。
其实这就是田径赛马的原理。
支持删除和二分查找,显然(set)。
然后分别从前后预处理应该出什么牌,最后枚举中介点就行。
问题出在具体实现上。
后一个过程其实可以用一个存负值的(set)来搞。
而不是像蒟蒻一样用一个存正值的(set)然后(TLE)半天。
还有(lower\_bound)没找到结果会返回(end())。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
#define ll long long
#define fp(i,a,b) for(int i=a;i<=b;++i)
#define fq(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int N=5e5+100;
int n,a[N],q[N],h[N],ans;
bool vis[N];
set<int>S1,S2;
set<int>::iterator it;
int main()
{
scanf("%d",&n);
fp(i,1,n) scanf("%d",&a[i]),vis[a[i]]=1;
fp(i,1,2*n) if(!vis[i]) S1.insert(i),S2.insert(-i);
fp(i,1,n)
{
it=S1.lower_bound(a[i]);
if(it!=S1.end())
{
q[i]=q[i-1]+1;
S1.erase(it);
}
else q[i]=q[i-1],S1.erase(*S1.begin());
}
fq(i,n,1)
{
it=S2.lower_bound(-a[i]);
if(it!=S2.end())
{
h[i]=h[i+1]+1;
S2.erase(it);
}
else h[i]=h[i+1],S2.erase(*S2.begin());
}
fp(i,0,n) ans=max(ans,q[i]+h[i+1]);
printf("%d
",ans);
return 0;
}