acm-(贪心、思维、构造)Codeforces Round #679 (Div. 2) D. Shurikens

13 篇文章 0 订阅
10 篇文章 0 订阅

题面
传送门
本题题意就是有一个空的盒子,给你 2 n 2n 2n个操作,每次操作要么往盒子里放一个数字,要么从盒子里拿一个最小的数字出来,保证两种操作都恰好有 n n n个,放入的数字为 1 ∼ n 1\sim n 1n,并且每次放入的数字保证不相同。输入中会模拟这个操作的过程,不过只会告诉你第二种操作拿出的最小数字的值,不会告诉你第一种操作放入了什么数字。题目就是要你通过输入判断第一种操作放入了什么数字,按怎样的顺序放入的。

对于一段操作:
+ + . . . + ( 假 设 有 b 1 个 + ) − a 1 , 1 − a 1 , 2 . . . − a 1 , c 1 + + . . . + ( 假 设 有 b 2 个 + ) − a 2 , 1 − a 2 , 1 . . . − a 2 , c 2 ⋮ \begin{aligned} &+\\ &+\\ &...\\ &+(假设有b_{1}个+)\\ &- a_{1,1}\\ &- a_{1,2}\\ &...\\ &-a_{1,c_1}\\ &+\\ &+\\ &...\\ &+(假设有b_2个+)\\ &-a_{2,1}\\ &-a_{2,1}\\ &...\\ &-a_{2,c_2}\\ &\vdots\\ \end{aligned} ++...+(b1+)a1,1a1,2...a1,c1++...+(b2+)a2,1a2,1...a2,c2
我们发现是一段 + + +一段 − - 的形式,想象将一段 − - 的数字 a a a全部堆叠在一起,就简化为如下所示:
图一
我们其实就是想把矩形中的小格子中的数字全部放到+号中去,并且还符合题意。
我们再抽象一下, + + +表示成线段的长度,矩形就不画出格子了,用矩形高度表示格子多少,知道就好,于是成为下图的情况。
图二
上图就非常简洁了,不过只画了三个柱子,后面其实还可能有很多,就不画了。
凡事先从简单的情况入手,我们先看只有两个柱子的情况,即下面这种。
图三
首先明白一个性质,柱子越底端的数字越小,这个很好理解,因为越底端的数字越早从盒子中拿出去,因此也越小,如果它比柱子上面的大就矛盾了。
于是柱子上的数字分布到横线上的时候,一定是柱子越底端的数字在横线上的分布离柱子越近,可以想象成柱子向左边倒下去,然后数字就刚好被分配到柱子倒下的位置。
现在我们只需要让这些柱子一个一个倒下去,看有哪些空位置就让数字分配过去就行了。

不过有个问题,到底是哪个柱子先倒为好呢?毕竟先倒的柱子对于横线上的空位有更大的选择权。对于两个柱子而言,我们发现无所谓先后顺序,假设第二个柱子倒下去的时候有一部分要跑到第一个柱子前面去,因为挤不下来了,就像下图所示:
图四
上图用箭头表示出了柱子上各个部分的数字对应分配的区域。由于第一个柱子和第二个柱子之间空间狭小导致只能容纳 B B B这个部分的数字,而 A A A部分的数字无论怎样都得跑到第一个柱子前边去,因此不管谁先倒其实无所谓,因为在同一块区域内的话数字怎样交换顺序都是不影响结果的。

那我们增加到三个柱子看看情况又如何。
图五
这时候情况就要复杂很多了,首先红色部分表明这是必定会“溢出”的一部分,也就是在当前柱子前的空间内容纳不下这部分,所以会被分配到前一个柱子的空间内。然后一定有 A A A部分内数字的最大值要小于 D D D部分内数字的最小值(假设最终输出答案是YES)。这个很容易想,因为 D D D部分会跑到第二块柱子前面去,但是它又没有在第二块柱子里,说明它的最小值都是非常大的,肯定比 A A A部分的最大值还大。同理有 C < A C<A C<A(简写)。不过 C , B , E C,B,E C,B,E部分的大小关系是未知的。

现在我们让第三个柱子先倒看看会发生什么, D D D部分自然会跑到第二个柱子前的区域去,于是第二个柱子溢出的不仅仅是 A A A部分了, B B B部分也可能溢出去一些,然后 B B B溢出去的那部分跑到第一个柱子前面了,那么是不是有可能这一部分会比 C C C的最大值更小呢?完全有可能,在这种情况下,第三个柱子倒就会导致矛盾产生。不过我们让第二个柱子倒情况就会好很多,首先 B B B不会产生溢出了,我们又早就保证 A > C A>C A>C,故 B B B倒下去后一切都还是很顺利的,然后让 A A A C C C倒下去(两者谁先倒无所谓了)的话会发现 D D D部分会跑到第一个柱子前面去,不过没关系,因为前面早就有关系 D > A > C D>A>C D>A>C成立,所以 D D D倒到第一个柱子前面都是不会影响结果的。

综上来看的话,只要结果是YES,那么选择让第一个柱子或第二个柱子先倒(两者谁先倒无所谓)一定能够保证产生合法的序列,当然结果为NO的话那直接输出就行了。

现在再来看四个柱子的情况:
图六
红色部分不解释了。首先根据红色部分判断结果是YES还是NO,如果不满足 C < A < D < G C<A<D<G C<A<D<G(这里的 X < Y X<Y X<Y是指 X X X中的最大值小于 Y Y Y中的最小值)那结果一定为NO。否则结果为YES,然后跟前面一样,我们直接构造一个合法方案就行了,就让柱子从左到右挨着顺序倒。毕竟前面分析了倒后面的柱子不优(可能产生矛盾),故我们看看直接倒前面的柱子会不会保证结果合法。
于是我们发现按从左到右一个一个柱子倒的方法一定是合法的,比如说对于 G G G而言,满足关系 G < D < E , G < A < B , G < C G<D<E,G<A<B,G<C G<D<E,G<A<B,G<C,什么意思呢, G G G放到前面任何地方其实都无所谓,不会影响到结果好坏,故这样倒下去产生的序列是合法的。

这其实是一种构造的思想,也许还可以通过倒的方式产生合法的序列,但是这种倒的方式肯定是正确无疑的。

具体实现的时候没必要这么麻烦的去判断那些溢出部分的大小关系,直接先构造,然后再模拟一下看是否合法即可。

具体代码如下:

int op[maxn*2];
int st[maxn*2],top=0,ans[maxn];
priority_queue<int,vector<int>,greater<int> >q;

int main(){
	int n=rd(),cnt=0;bool failed=false;
	char ch[5];
	FOR(i,1,2*n+1){
		rds(ch);
		if(ch[0]=='-'){
			int u=rd();
			op[i]=u;
			if(!top){
				failed=true;
				continue;
			}
			ans[st[top--]]=u;
		}else{
			st[++top]=++cnt;
		}
	}
	if(failed)return wrsn("NO"),0;
	int tot=0;
	FOR(i,1,2*n+1){
		if(!op[i]){
			q.push(ans[++tot]);
		}else{
			if(q.top()!=op[i])return wrsn("NO"),0;
			q.pop();
		}
	}
	wrsn("YES");
	FOR(i,1,n+1)wr(ans[i]),putchar(' ');
	wrsn("");
} 
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值