luogu P7514 [省选联考 2021 A/B 卷] 卡牌游戏
题面传送门
居然省选出这么简单的题目。
首先显然这个东西有单调性所以可以二分。
然后考虑这个东西怎么验证。
首先因为\(a\)升序所以可以直接在\(a\)上面找范围。
考虑确定左端点\(l\)则右端点\(r\)很好确定。
我们无非要确定两件事情:翻的数量有没有超过\(m\),翻完之后有没有出现不在范围内的情况。
前面那个显然很好维护。但是后面那个并不是简单的加加减减就可以维护的。
可以发现\([1,l-1]\)与\([r+1,n]\)范围内的东西都是要翻的,所以处理前缀后缀\(b\)的最小值和最大值即可。
然后要以右端点为最大值再扫一遍。
时间复杂度\(O(nlogn)\)
code:
#include<cstdio>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 1000039
#define I inline
using namespace std;
int n,m,k,x,y,z,a[N],b[N],l,r,mid,q1[N],q2[N],q3[N],q4[N];
I void read(int &x){
char s=getchar();x=0;
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
I int check(int mid){
int r,i;
for(r=i=1;i<=n;i++){
while(r<=n&&a[r]<=a[i]+mid) r++;
if(i+n-r>m) continue;
if(min(q2[i-1],q4[r])>=a[i]&&max(q1[i-1],q3[r])<=a[i]+mid) return 1;
}
for(r=i=1;r<=n;r++){
while(i<=n&&a[i]+mid<a[r]) i++;
if(n-r+i-1>m) continue;
if(min(q2[i-1],q4[r+1])>=a[r]-mid&&max(q1[i-1],q3[r+1])<=a[r]) return 1;
}
return 0;
}
int main(){
freopen("1.in","r",stdin);
register int i;scanf("%d%d",&n,&m);q2[0]=q4[n+1]=1e9;
for(i=1;i<=n;i++)read(a[i]);
for(i=1;i<=n;i++) read(b[i]),q1[i]=max(q1[i-1],b[i]),q2[i]=min(q2[i-1],b[i]);l=-1;r=1e9;
for(i=n;i;i--) q3[i]=max(q3[i+1],b[i]),q4[i]=min(q4[i+1],b[i]);
while(l+1<r) mid=l+r>>1,check(mid)?(r=mid):(l=mid);
printf("%d\n",r);
}