正题
题目链接:https://www.luogu.com.cn/problem/P7514
题目大意
给出nnn个卡牌有ai/bia_i/b_iai/bi,开始都是aia_iai朝上,将不超过mmm张卡牌变为bib_ibi面朝上,使得朝上的数字中最大值减去最小值最小。
3≤n≤106,1≤m<n,1≤ai,bi≤1093\leq n\leq 10^6,1\leq m<n,1\leq a_i,b_i\leq 10^93≤n≤106,1≤m<n,1≤ai,bi≤109
解题思路
虽然数据比较水,但是题目也是一道比较水的贪心题。
先离散化然后考虑暴力点的想法。枚举最大值和最小值l,rl,rl,r,那么对于aia_iai在[l,r][l,r][l,r]之间的自然是不理,在之外的一定需要翻,如果翻转后存在bib_ibi不在[l,r][l,r][l,r]之间,那么显然行不通。
这样我们就可以O(n2)O(n^2)O(n2)了。
不难发现假设[l,r][l,r][l,r]行的通那么[l,r+1][l,r+1][l,r+1]用原来的方法一定也行得通,所以对于rrr递增,lll也是不降的,双指针维护一下就好了。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=2e6+10;
int n,m,ans,a[N],b[N],c[N],mx[N],mi[N],nx[N],ni[N],pos[N];
int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
bool cmp(int x,int y)
{return a[x]<a[y];}
bool check(int l,int r){int L=pos[l],R=pos[r+1]-1;int k=m-(n-R)-(L-1);if(k<0)return 0;if(mx[L-1]>r||mi[L-1]<l)return 0;if(nx[R+1]>r||ni[R+1]<l)return 0;return 1;
}
int main()
{n=read();m=read();for(int i=1;i<=2*n;i++)a[i]=b[i]=read(),c[i]=i;sort(c+1,c+1+2*n,cmp);int cnt=2*n;for(int i=1;i<=2*n;i++)a[c[i]]=i;mi[0]=ni[n+1]=1e9+7;for(int i=1;i<=n;i++)mx[i]=max(a[i+n],mx[i-1]);for(int i=1;i<=n;i++)mi[i]=min(a[i+n],mi[i-1]);for(int i=n;i>=1;i--)nx[i]=max(a[i+n],nx[i+1]);for(int i=n;i>=1;i--)ni[i]=min(a[i+n],ni[i+1]);for(int i=1;i<=n;i++)pos[a[i]]=i;pos[cnt+1]=n+1;for(int i=cnt;i>=1;i--)if(!pos[i])pos[i]=pos[i+1];ans=1e9+7;int z=0;for(int i=1;i<=cnt;i++){while(z<i&&check(z+1,i))z++;if(z)ans=min(ans,b[c[i]]-b[c[z]]);}printf("%d\n",ans);
}