CH134 双端队列 题解报告
【题目大意】
用若干个双端队列给$N$个整数排序,依次处理这$N$个数,对于每个数$A_i$,可以进行两种操作:
1.新建一个双端队列,并将$A_i$作为这个队列中唯一的数
2.把$A_i$从已有队列的队头或队尾入队
对所有的数处理完后,要求这些队列能够按照一定的顺序连接起来,得到一个非降的长度为$N$的序列,求最少需要多少个双端队列。
【思路分析】
我们把问题反过来思考,先把$N$个数从小到大排序,然后分成尽量少的几段,对应原问题中的合法双端队列。
易知一个结论,对于排序后每个位置的数原本的下标组成的序列$B$,如果一段满足单谷性质(即先递减后递增),那么这一段就对应原问题中的一个合法双端队列。(递减的一段相当于从队头插入,递增的一段相当于从队尾插入)
还要注意一点,就是如果存在相同的几个数,那么它们排序后的位置是随机的,可以看成一个整体来处理。如果这个整体中最小的下标大于前面的序列中最大的下标,那么满足递增;如果这个整体中最大的下标小于前面的序列中最小的下标,那么满足递减。
【代码实现】
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define g() getchar() 7 #define rg register 8 #define go(i,a,b) for(rg int i=a;i<=b;i++) 9 #define back(i,a,b) for(rg int i=a;i>=b;i--) 10 #define db double 11 #define ll long long 12 #define il inline 13 #define pf printf 14 using namespace std; 15 int fr(){ 16 int w=0,q=1; 17 char ch=g(); 18 while(ch<'0'||ch>'9'){ 19 if(ch=='-') q=-1; 20 ch=g(); 21 } 22 while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=g(); 23 return w*q; 24 } 25 const int N=200002; 26 int n; 27 struct number{ 28 int a,id; 29 }d[N]; 30 struct line{ 31 int minn,maxn; 32 }l[N]; 33 il bool cmp(number x,number y){ 34 return x.a<y.a; 35 } 36 int main(){ 37 //freopen("","r",stdin); 38 //freopen("","w",stdout); 39 n=fr(); 40 go(i,1,n) d[i].a=fr(),d[i].id=i; 41 sort(d+1,d+1+n,cmp); 42 rg int num=0; 43 go(i,1,n){//先处理一下相同的数字 44 l[++num].minn=d[i].id,l[num].maxn=d[i].id; 45 while(d[i].a==d[i+1].a){ 46 i++; 47 l[num].minn=min(l[num].minn,d[i].id); 48 l[num].maxn=max(l[num].maxn,d[i].id); 49 } 50 } 51 bool now=0;rg int ans=1; 52 go(i,2,num){ 53 if(now){//递增 54 if(l[i].minn>l[i-1].maxn) continue; 55 else ans++,now=0; 56 } 57 else{//递减 58 if(l[i].maxn<l[i-1].minn) continue; 59 else now=1; 60 } 61 } 62 pf("%d\n",ans); 63 return 0; 64 }