D. Shurikens 思维
题目大意:
给一个N,代表有N个物体,编号1->N。
接下来是2*N行,遇到 + 代表将某一物品放到桌子上,遇到减号和数字,代表从桌子上把编号为这个数字的物品拿走了。
问在每一个加号后面能否加上一个数字 ,代表将序号为这个数字的物品放到桌子上,使得这个拿东西和放东西的操作序列符合(每次拿走物品都是拿走桌子上当前序号最小的物品,并且拿东西的时候有东西可拿)这个条件,如果可以输出YES,输出数字序列,如果不可以输出NO即可。
题解:
写这个题目的题解不是因为这个题目难,而是因为这个题目的一个解法我觉得很有意思,但是我又没想到。
我写的解法很复杂,单调栈 + 线段树。
- 单调栈维护一下递减的序列,然后对于每一个 - x,找到大于 x 的第一个位置的后面一个位置 (l),然后贪心的考虑在区间 ([l,i]) ( (i) 表示此时这个位置) ,找到最靠前面的一个位置分配给他。
然后上网搜了一下,发现一个很简洁的题解:
https://www.bilibili.com/read/cv8098598
如果不太理解可以看看:https://blog.csdn.net/qq_30361651/article/details/109292134
- 如果是 YES,那么对于第 (i) 个人 (- x) ,那么前面离它最近的 (+) 的值可以设为 (x)
这个是为什么呢?
- 首先对于连续的 (-x) ,那么后面的值一定比前面的小
- 如果两段,假设第一段为 (A) 第二段设为 (B) 第三段设为 (C),那么如果 (C) 有一部分要在 (AB) 之间,那么可以确定的是 (C) 一定比 (B) 的值大,如果小,那么就是 NO。
- 如果这部分被 (C) 占了,那么 (B) 的一部分就在 (A) 前面,说明 (B) 大于 (A)
- 如果这部分被 (B) 占了,那么如果 (B) 可以往前推,那么 (C) 一定也可以往前推, (C) 大于 (A)
solution1
#include <bits/stdc++.h>
#define lson (id<<1)
#define rson (id<<1|1)
using namespace std;
const int maxn = 2e5 + 10;
/*
* 单调栈维护 x,维护一个单调递减的
* 线段树更新值,更新每一个位置 + 的数字
*/
int que[maxn],sum[maxn * 4],a[maxn],len[maxn * 4],r,ans[maxn];
void build(int id,int l,int r) {
len[id] = r - l + 1, sum[id] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
}
void update(int id,int l,int r,int pos) {
if (pos < l || pos > r) return;
if (l == r) {
sum[id] = 1;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(lson, l, mid, pos);
else update(rson, mid + 1, r, pos);
sum[id] = sum[lson] + sum[rson];
}
int modify(int id,int l,int r,int x,int y,int v) {
if(x>r||y<l) return 0;
// printf("modify:id = %d l = %d r = %d x = %d y = %d v = %d
",id,l,r,x,y,v);
if (l == r && sum[id] == 0) {
// printf("l = %d r = %d
",l,r);
ans[l] = v,sum[id] = 1;
return true;
}
int mid = (l + r) >> 1, ans = 0;
if (x <= mid && sum[lson] < len[lson]) ans = modify(lson, l, mid, x, y,v);
if (ans == 0 && y > mid && sum[rson] < len[rson]) ans = modify(rson, mid + 1, r, x, y,v);
sum[id] = sum[lson] + sum[rson];
return ans;
}
int ok(int x) {
int lc = 1, rc = r, ans = 0;
while (lc <= rc) {
int mid = (lc + rc) >> 1;
if (a[que[mid]] > x) lc = mid + 1, ans = que[mid];
else rc = mid - 1;
}
return ans;
}
void debug(int id,int l,int r){
if(l==r){
printf("id = %d l = %d r = %d sum = %d
",id,l,r,sum[id]);
return ;
}
int mid = (l+r)>>1;
debug(lson,l,mid);
debug(rson,mid+1,r);
}
int main() {
int n;
scanf("%d", &n);
build(1, 1, 2 * n);
for (int i = 1; i <= 2 * n; i++) {
char s[10];
scanf("%s", s + 1);
if (s[1] == '+') a[i] = -1;
else scanf("%d", &a[i]), update(1, 1, 2 * n, i);
}
r = 0;
que[r] = 0;
bool flag = true;
for (int i = 1; i <= 2 * n; i++) {
if (a[i] > 0) {
int pos = ok(a[i]) + 1;
int res = modify(1, 1, 2 * n, pos, i,a[i]);
if (!res) {
flag = false;
break;
}
while (r > 0 && a[i] >= a[que[r]]) r--;
que[++r] = i;
}
}
if(!flag) printf("NO
");
else {
printf("YES
");
for(int i=1;i<=2*n;i++){
if(a[i] < 0) printf("%d ",ans[i]);
}
printf("
");
}
}
solution2
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int ans[maxn];
int main(){
int n;
scanf("%d",&n);
stack<int>sta;
for(int i=1;i<=2*n;i++){
char s[10];
scanf("%s",s+1);
if(s[1] == '+') sta.push(i);
else{
scanf("%d",&ans[i]);
if(sta.empty()){
printf("NO
");
return 0;
}
ans[sta.top()] = -ans[i];
sta.pop();
}
}
set<int>st;
for(int i=1;i<=2*n;i++){
if(ans[i] < 0) st.insert(-ans[i]);
else{
if(st.empty() || *st.begin() != ans[i]){
printf("NO
");
return 0;
}
st.erase(st.begin());
}
}
printf("YES
");
for(int i=1;i<=2*n;i++){
if(ans[i] < 0) printf("%d ",-ans[i]);
}
printf("
");
}