P7514 [省选联考 2021 A/B 卷] 卡牌游戏

[省选联考 2021 A/B 卷] 卡牌游戏

题目描述

Alice 有 n 张卡牌,第 i1in)张卡牌的正面有数字 ai,背面有数字 bi,初始时所有卡牌正面朝上。

现在 Alice 可以将不超过 m 张卡牌翻面,即由正面朝上改为背面朝上。Alice 的目标是让最终朝上的 n 个数字的极差(最大值与最小值的差)尽量小。请你帮 Alice 算一算极差的最小值是多少。

【数据范围】

对于所有测试数据:3n1061m<n1ai,bi109

每个测试点的具体限制见下表:

测试点编号 n 特殊限制
12 10
34 500
56 5×105 m1000
7 105
8 4×105
9 7×105
10 106

思路:

首先观察一下题目,我们很容易就会发现这个答案是具有单调性的,也就是对于 x,xR,check(x)={0,0,0,0,0,1,1,1}

然后我们就可以考虑二分。

二分一个答案代表上界,然后现在问题就转换为如何判断原序列经过若干次反转后极差能否 <n

为了解决这个问题,我们需要引出一个结论:最后翻转的区间一定最开始的一段和最后面的一段。

下面来简答证明一下这个结论的正确性(其实还是比较感性的)

首先我们先设写出一个序列:
00001111100000

我们令 0 表示翻转这个位置,1 表示不翻转

设翻转的位置为 i,最左边且未翻转的值为 y,最右边且未翻转的值为 x。左边翻转的最大值为 mx1,左边翻转的最小值为 mn1;右边翻转的最大值为 mx2,右边翻转的最小值为 mn2

然后我们列出在不反转 i 的时候答案应该是多少:max(x,mx1,mx2)min(y,mn1,mn2)

我们假设翻转过后的 bi>x,则原式变为 max(bi,mx1,mx2)min(y,mn1,mn2)
然后我们不难发现,如果我们在这个位置单独翻转和从右侧包含过来的情况其实应该是等价的,即依然可以转换为上述一开始的式子。

如果 bi<y 同理。

所以梳理一下解法:

  • 二分一个上界
  • 固定分界点 l,然后尺取分界点 r
  • 固定分界点 r,然后尺取分界点 l
放一下代码:
点击查看代码
#include<bits/stdc++.h> #define int long long #define mem(a) memset(a,0,sizeof(a)) #define set(a,b) memset(a,b,sizeof(a)) #define ls i<<1 #define rs i<<1|1 #define pb push_back #define pt putchar #define All(a) a.begin(),a.end() #define T int t;cin>>t;while(t--) #define rand RAND using namespace std; char buf[1<<20],*p1,*p2; #define gc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++) template<class Typ> Typ &re(Typ &x){char ch=gc(),sgn=0; x=0;for(;ch<'0'||ch>'9';ch=gc()) sgn|=ch=='-';for(;ch>='0'&&ch<='9';ch=gc()) x=x*10+(ch^48);return sgn&&(x=-x),x;} template<class Typ> void wt(Typ x){if(x<0) putchar('-'),x=-x;if(x>9) wt(x/10);putchar(x%10^48);} const int inf=1e18; const int maxn=1e6+5; const int mod=1e9+7; int seed = 19243; unsigned rand(){return seed=(seed*48271ll)%2147483647;} int n,m; int a[maxn],b[maxn]; int pre_mn[maxn],pre_mx[maxn]; int suf_mn[maxn],suf_mx[maxn]; void RMQ(){ for(int i=0;i<=n+1;i++)pre_mn[i]=suf_mn[i]=inf,pre_mx[i]=suf_mx[i]=-inf; for(int i=1;i<=n;i++)pre_mn[i]=min(pre_mn[i-1],b[i]),pre_mx[i]=max(pre_mx[i-1],b[i]); for(int i=n;i>=1;i--)suf_mn[i]=min(suf_mn[i+1],b[i]),suf_mx[i]=max(suf_mx[i+1],b[i]); } bool check(int x){ for(int l=1,r=1;l<=n;l++){ while(r<=n&&a[r]-a[l]<=x){ if((n-(r-l+1)<=m) and max({a[r],pre_mx[l-1],suf_mx[r+1]})-min({a[l],pre_mn[l-1],suf_mn[r+1]})<=x)return 1; r++; } r--; } for(int r=n,l=n;r>=1;r--){ while(l>=1&&a[r]-a[l]<=x){ if((n-(r-l+1)<=m) and max({a[r],pre_mx[l-1],suf_mx[r+1]})-min({a[l],pre_mn[l-1],suf_mn[r+1]})<=x)return 1; l--; } l++; } return 0; } signed main(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)cin>>b[i]; RMQ(); int l=0,r=a[n]-a[1],ans=0; while(l<=r){ int mid=(l+r)/2; if(check(mid))ans=mid,r=mid-1; else l=mid+1; } cout<<ans<<endl; return 0; }

__EOF__

本文作者Candycar
本文链接https://www.cnblogs.com/Candycar/p/17823012.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Candycar  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示