题解:P3551 [POI 2013] USU-Take-out

题目传送门

洛谷题解传送门

题意

每次消除 \(k+1\) 个砖,其中 \(k\) 块白色,\(1\) 块黑色,并且这 \(k+1\) 块砖从开始到结束,中间不能路过已经消除过的砖。

思路

对照样例,不难想到了一点:如果某连续的一段是可消除的,则可以将这一段放到最后再消除。

这是样例输出:

1 8 12
2 6 7
3 4 5
9 10 11

图示:

最后两步消除的部分都是连续段。由此或许可以看出一些性质?

我们考虑递归:

  1. 对于一个可以消除的连续子段,我们将它放在最后消除显然是可行的。
  2. 考虑时光倒流,消除了这个连续子段后将其直接丢掉,这样导致的结果就是会出现新的可以消除的连续子段,我们再将其放在序列中上一次的消除操作的前一次操作,这样就恰好可以满足条件了。

这么看来,我们的操作序列就是一个,同时我们可以在输入的过程中也实现一个栈,以便于我们的删除操作。

考虑满足什么条件的连续子段可以消除:\(k\) 块白色,\(1\) 块黑色。也就是 \(k+1\) 块砖之中只有一块黑色或有 \(k\) 块白色。于是考虑维护一个前缀和数组,记录前缀白色砖或黑色砖的块数(我的代码实现中是记录的黑色砖个数)。

整理一下,在删除的时候需要满足以下几个条件:

  1. 是连续段。
  2. 恰有 \(1\) 块黑色砖(或恰有 \(k\) 块白色砖)。

于是这个题做完了。

代码实现

#include <bits/stdc++.h>
#define loop(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define rloop(i,a,b) for(int i=(a);i>=(int)(b);i--)
using namespace std;
typedef long long ll;
const int N=1e6+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,k,sum[N];
char c;
stack<int>st,ans;
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>k;
	loop(i,1,n){
		cin>>c;
		st.push(i);
		int s=st.size();
		sum[s]=sum[s-1]+(int)(c=='c');
		if(s>=k+1&&sum[s]-sum[s-k-1]==1){
			rloop(j,s,s-k)ans.push(st.top()),st.pop();
		}
	}
	int T=0;
	while(!ans.empty()){
		cout<<ans.top()<<' ';
		ans.pop();
		T=(T+1)%(k+1);
		if(!T)cout<<'\n';
	}
	return 0;
}

完结撒花花!

posted @ 2026-03-19 13:42  Circle_Table  阅读(4)  评论(0)    收藏  举报