此题目来自bzoj.org/p/Z2149
题目
类似电脑中的"空档接龙“游戏
给定N个数字,其为1到N的某个全排列。
对于每一个数字,找到已经生成的栈中栈顶大于等于该数字的最小值,并将该数字存入到栈顶
每当一个栈内的数据总量达到K时,则“吃掉”这个栈,被“吃掉”的栈不能再存入新元素。
依次输出从1到N每一个元素被“吃掉”的时间。(对于没被吃的元素输出-1)
N<=1e6
小插曲:1e6==1000000==10^6
如果有人做过“奇怪的排序”会发现十分十分的相似。
这道题同样用到单调栈。
这题要用到很多的栈,有一些人就开很多个栈,然后被炸的找不到北(你问我为什么知道?因为我就被炸过……)
其实,我们可以用几个数组来实现n个栈:
sizen[x]=y,代表第x个栈的大小
ans[x]=y,代表数字x在第y个时间被消去
under[x]=y,代表在栈中,数字x的下面是哪个数字
top[x]=y,代表在第x个栈中,其栈顶的元素的y
idx:代表一共有多少上栈
核心思路:
无论如何开栈,最后的读数永远是从左往右,从顶到底的弹出。
所以,我们的单调栈必须是严格上升的,为了保证最优解,每一个数要放在栈顶元素大于这个数且栈顶元素尽量小的栈里。
于是每读入一个元素x,看其是否大于top[idx]
如果是的,则新开一个栈出来,并记录相关信息。
否则在top数组中进行二分查找,得到数字T
代表x应该被加入哪个栈中,于是这个栈要变大
并更换栈顶元素为x,并记录under[x]=top[T]
最后看下size[t]是否等于k,如果满足话
则将栈中所有元素倒出来,并记录其在第i个时间被 消掉
最最后在将top数组中将所有[t+1,idx]间的元素
左移一位,注意栈的栈顶元素,栈的大小要更新信息
代码(我的):
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int cd[N],ans[N],under[N],top[N],idx,sum;
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
sum++;
int x;
cin>>x;
if(top[idx]<x){
idx++;
top[idx]=x;
cd[idx]=1;
if(cd[idx]>=k){
int w=top[idx];
ans[w]=sum;
idx--;
}
}
else{
int l=1,j=idx,T=0;
while(l<=j){
int mid=(l+j)/2;
if(top[mid]>x)
j=mid-1,T=mid;
else
l=mid+1;
}
under[x]=top[T];
top[T]=x;
cd[T]++;
if(cd[T]>=k){
int w=top[T];
while(w!=0){
ans[w]=sum;
w=under[w];
}
for(int j=T;j<=idx;j++){
cd[j]=cd[j+1];
top[j]=top[j+1];
}
idx--;
}
}
}
for(int i=1;i<=n;i++){
if(ans[i]==0)
cout<<-1<<endl;
else
cout<<ans[i]<<endl;
}
return 0;
}
经过优化的:
#include <bits/stdc++.h>
#define N 200007
using namespace std;
int n, k, x, sizen[N], ans[N], under[N], top[N], idx;
int main() {
memset(ans, -1, sizeof ans);
scanf("%d%d", &n, &k);
for ( int i = 1, t; i <= n; ++i)
{
scanf("%d", &x);
if(x > top[idx])
//开出一个top数组,存在每个栈的栈顶元素
{
top[++idx] = x;
sizen[idx] = 1;
t=idx;
}
else
{
t = upper_bound(top+1,top+1+idx,x)-top;//找出x应该放在哪个栈中
sizen[t]++;
under[x] = top[t];
top[t] = x;
}
if(sizen[t] == k) //如果x所在的栈的元素有了k个
{
while(x)
{
ans[x] = i; //第x个元素在第i个时间被消掉
x = under[x]; //取出x下方的元素
}
for ( int j = t; j < idx; ++j)
top[j] = top[j + 1],sizen[j]=sizen[j+1];
--idx;
}
}
for ( int i = 1; i <= n; ++i)
printf("%d\n", ans[i]);
return 0;
}