题目
输入输出样例
题目大意
需要构造一个序列
a
1
a_1
a1,
a
2
a_2
a2,…,
a
n
a_n
an。
a
i
a_i
ai入栈时,如果栈顶的元素比它大,则将栈顶元素弹出,直到栈为空或者栈顶元素小于等于它,此时记录栈的大小为
b
i
b_i
bi。
题目给出n,k,代表需要构造的序列的长度和已知k个的b数组的值,接下来k行,分别给出两个数字p,q,代表
b
p
b_p
bp=q,即
a
p
a_p
ap按要求入栈后,栈的大小为q。
题目思路
最初的时候,我们认为 b i b_i bi=x,就代表构造出的a序列中,在第i为前面,有x-1个数字比 a i a_i ai小。按照这样的思路,我们构造一个1~n的序列,按照下标大小,从后往前遍历给出的 b i b_i bi,选出剩余数字中第 b i b_i bi小的数字,放在 a i a_i ai。用线段树,维护区间内有多少数字未被使用。但是这种思路不太正确,因为没有考虑弹栈的情况,先贴出错误代码:
#include <bits/stdc++.h>
#include<iostream>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const int MAXN = 1e6+3;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int pre[MAXN],ans[MAXN],xb[MAXN];
set<int> s;
struct {
int l,r,len;
}tree[4*MAXN];
//建树,树内存的值是该区间内还剩余的数的长度
void build(int node,int l,int r)
{
tree[node].l=l;
tree[node].r=r;
tree[node].len=r-l+1;
if(l==r)
return ;
int mid=(l+r)>>1;
build(node<<1,l,mid);
build((node<<1)+1,mid+1,r);
}
int query(int node,int num)
{
tree[node].len--;//要选择的数一定在该区间内,所以长度减1
if(tree[node].l==tree[node].r)
return tree[node].l;
//如果左子树内数字的个数小于 排序数num,则查询右子树,排序数也要更新
if(tree[node<<1].len<num)
return query((node<<1)+1,num-tree[node<<1].len);
else
return query(node<<1,num);
}
int main()
{
int n,p,q,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
s.insert(i);
bool flag=false;
for(int i=1;i<=k;i++)
{
cin>>p>>q;
if(p<q)
flag=true;
pre[p]=q;
xb[i]=p;
}
if(flag)
{
cout<<"-1"<<endl;
}
else{
sort(xb+1,xb+k+1);
build(1,1,n);
int tmp=0;
for(int i=k;i>=1;i--)//从后往前遍历
{
tmp=xb[i];
if(tree[1].len<pre[tmp])
{
flag=1;
break;
}
ans[tmp]=query(1,pre[tmp]);
s.erase(ans[tmp]);
}
if(flag)
cout<<-1<<endl;
else{
int cnt=1;
auto it=s.begin();
if(ans[1]) cout<<ans[1];
else
{
cout<<*it;
it++;
}
while(cnt<n)
{
cnt++;
if(ans[cnt])
cout<<" "<<ans[cnt];
else
{
cout<<" "<<*it;
it++;
}
}
}
}
return 0;
}
后来和其他大佬讨论,发现了一种类似于拓扑的思想
先看看例子吧:
按照这种思想,附上AC代码:
#include <bits/stdc++.h>
#include <iostream>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const int MAXN = 1e6 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int b[MAXN],ans[MAXN];
struct node{
int xb;
int num;
bool operator < (node b) const//根据下标排序
{
return xb<b.xb;
}
}point[MAXN];
int main()
{
// freopen("in.txt", "r", stdin);
qc;
int n,k;
cin>>n>>k;
int p,x;
bool f=false;
for(int i=1;i<=k;i++)
{
cin>>p>>x;
point[i].xb=p;
point[i].num=x;
if(p<x)//单调栈的大小不会大于下标
f=true;
b[p]=x;
}
if(f)
{
cout<<-1<<endl;
return 0;
}
sort(point+1,point+k+1);
int pos1,pos2;
point[k+1]=node{n+1,0};
for(int i=1;i<=k;i++)
{
pos1=point[i].xb;
pos2=point[i+1].xb;
if(pos2-pos1 < point[i+1].num-point[i].num)//判断非法
{
f=true;
break;
}
if(point[i+1].num<=point[i].num)//如果比下一个栈的大小大,那么pos1到pos2-1都相同,都要弹出的
{
for(int j=pos1+1;j<pos2;j++)
b[j]=b[j-1];
}
else//如果递增,则中间按照差值依次填补
{
for(int j=pos2-1;j>pos1;j--)
{
b[j]=b[j+1]-1;
b[j]=max(b[j],b[pos1]);
}
}
}
for(int j=point[1].xb-1;j>=1;j--)//补最前面一段的b值
{
b[j]=b[j+1]-1;
b[j]=max(b[j],1);
}
if(f)
cout<<"-1"<<endl;
else{
int maxn=0;
for(int i=1;i<=n;i++)
maxn=max(maxn,b[i]);
vector<int> v[maxn+1];
for(int i=n;i>=1;i--)
v[b[i]].push_back(i);
int now=1;
for(int i=1;i<=maxn;i++)
{
int cnt=v[i].size();
for(int j=0;j<cnt;j++)
ans[v[i][j]]=now++;
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
}
return 0;
}
碎碎念:
开局半小时写了两个,这一题花了我快三个小时还没AC…怪我太菜,orz