P7912 [CSP-J 2021] 小熊的果篮 题解

题目链接

我的博客

思路

看到这道题的第一眼,笔者就认为是一个链表。但是打完发现复杂度是假的。
最慢情况下,复杂度可能退化成 \(O(n^2)\) 的。

60ptsTLE链表做法

const int N=2e5+10;
int n,cnt;
bool vis[N];
struct node{
	int nxt,w;
}p[N];
void work(int x){//x 为当前链表第一个数 
	vector ans;
	int op=-1;
	cnt++;
	vis[x]=1;
	op=p[x].w;//水果种类
	ans.push_back(x);
	int lst=x;
	x=p[x].nxt;
	for(int i=x;i<=n;i=p[i].nxt){
		if(vis[i]) continue;//已经出链表
		if(p[i].w==op) {
			lst=i;
			continue;
		}
		p[lst].nxt=p[i].nxt;//更新链表
		vis[i]=1;
		op=p[i].w;
		ans.push_back(i);
		cnt++;
	}
	bool fl=0;
	for(auto x:ans){
		if(!fl) {
			printf("%d",x);
			fl=1;
		}
		else printf(" %d",x);
	}
	puts("");
}
signed main(){
//	freopen("fruit.in","r",stdin);
//	freopen("fruit.out","w",stdout);
	n=Read();
	for(int i=1;i<=n;i++){
		p[i].w=Read();
	}
	for(int i=1;i<=n;i++) p[i].nxt=i+1;
	for(int i=1;i<=n;i++){
		if(cnt>=n) break;//如果都输出了,直接 break
		if(vis[i]) continue;
		work(i);
	}
	return 0; 
} 
  

考虑正解。

我们可以把一个块看成一个整体。

因为队列先进先出的性质,于是我们可以用队列来模拟。

当队列中的一个块都取出后,就可以合并它的前一个块和后一个块。

代码

const int N=2e5+10;
int n,cnt,a[N];
bool vis[N];
struct node{
	int st,en;//start,end 位置
	int op;//水果种类
};
queue<node> q1,q2;//两个队列维护
signed main(){
	n=Read();
	for(int i=1;i<=n;i++){
		a[i]=Read();
	}
	a[n+1]=!a[n];//注意这里,因为入队的时候可能有 n+1
	int lst=1;
	for(int i=1;i<=n;i++){
		if(a[i]!=a[i+1]){
			q1.push((node){lst,i,a[i]});
			lst=i+1;
		}
	}
	while(cnt<n){//cnt:输出的个数
		while(!q1.empty()){
			node t=q1.front();q1.pop();
			while(vis[t.st]&&t.st<=t.en) t.st++;//已经被取了
			if(t.st>t.en) continue;//这个块取完了
			printf("%d ",t.st);
			cnt++;
			vis[t.st]=1;
			t.st++;
			if(t.st>t.en) continue;//这个块取完了
			q2.push(t); //存到 q2 合并
		}
		puts("");
		while(!q2.empty()){
			node t1=q2.front();q2.pop();
			while(!q2.empty()){
				node t2=q2.front();
				if(t1.op==t2.op){//如果种类相同,直接合并
					t1.en=t2.en;
					q2.pop();
				}
				else break;
			}
			q1.push(t1);//回到 q1 里面
		}
	}
	return 0; 
} 
posted on 2025-11-12 15:32  _Liuliuliuliuliu  阅读(10)  评论(0)    收藏  举报