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

题目传送门

正解

思路

开两个链表,维护单个水果和每个块的最左端,暴力模拟即可。

难点主要在于每个块最左端的合并。

令当前需要删除的位置为 NOW ,那么:

  • NOW 的前驱的后继的处理:

    1. 如果 NOW 无前驱:那还管他干什么呢,直接略过
    2. 如果 NOW 有前驱但无后继:该前驱将不会有后继,指向 END
    3. 如果 NOW 有前驱但只有一个后继:不难证明该后继会与该前驱合并,而且合并后该前驱将不会再有后继,所以指向 END
    4. 如果 NOW 有前驱但有两个及以上的后继:与 iii 相似,但是合并后将仍有后继,该后继即 NOW 的后继 的后继
  • NOW 的后继的前驱的处理:

    实际上,当且仅当 NOW 没有前驱的时候它才会指向 START,其余情况均不变

  • NOW 的后继的后继的前驱的处理:

    • 假定它存在,那么:
    1. 如果 NOW 无前驱:显然应该是 NOW 的后继

    2. 如果 NOW 有前驱:显然应该是 NOW 的前驱

将上述三段整合为几个三目套三目,即可。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int Hen_hen_eaaaaa(233333);
struct Eat{
	int Now,Nxt,Bef,Col;//位置、前驱、后继  
}eat[Hen_hen_eaaaaa],frt[Hen_hen_eaaaaa];
int n,cnt,STT;
bool NOW;//当前水果(用于建立不同块 
inline int R(){
	int x=0,f=1;char c='c';
	while(c>'9'||c<'0'){f=f*(c=='-'?-1:1);c=getchar();}
	while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int main(){
	n=R();
	if(!n) return 0;
	for(int i=1;i<=n;++i){frt[i].Col=R();frt[i].Bef=i-1;frt[i-1].Nxt=i;}
	NOW=!frt[1].Col;frt[0].Nxt=1;
	for(int i=1;i<=n;++i){
		if(frt[i].Col!=NOW){
			NOW=frt[i].Col;
			eat[++cnt].Now=i;
			eat[cnt].Bef=cnt-1;
			eat[cnt-1].Nxt=cnt;
		}
	}
	int N,NN,NNN;
	while(frt[0].Nxt&&eat[0].Nxt){
		N=eat[0].Nxt;
		while(N){
			printf("%d ",eat[N].Now);
			NN=eat[N].Now;
			frt[frt[NN].Bef].Nxt=frt[NN].Nxt;
			frt[frt[NN].Nxt].Bef=frt[NN].Bef;
			eat[N].Now=frt[NN].Nxt;
			NNN=eat[N].Now;
			if(((frt[NNN].Col!=frt[NN].Col)||(!NNN))&&((frt[eat[eat[N].Bef].Now].Col!=frt[NN].Col)||(!eat[N].Bef))){
				eat[eat[N].Bef].Nxt=(eat[N].Bef?((eat[eat[N].Nxt].Nxt&&eat[N].Nxt)?eat[eat[N].Nxt].Nxt:0):eat[N].Nxt);
				eat[eat[eat[N].Nxt].Nxt].Bef=((eat[N].Bef&&eat[eat[N].Nxt].Nxt&&eat[N].Nxt)?eat[N].Bef:eat[eat[eat[N].Nxt].Nxt].Bef);
				eat[eat[N].Nxt].Bef=((eat[N].Bef||!eat[N].Nxt)?eat[eat[N].Nxt].Bef:0);
			}//最难の合并操作
			N=eat[N].Nxt;
		}
		printf("\n");
	}
	return 0;
}
posted @ 2021-11-06 20:32  Binaries  阅读(648)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end