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;
}
浙公网安备 33010602011771号