专栏文章
题解:AT_abc421_f [ABC421F] Erase between X and Y
AT_abc421_f题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minyj92i
- 此快照首次捕获于
- 2025/12/02 10:26 3 个月前
- 此快照最后确认于
- 2025/12/02 10:26 3 个月前
CF 做不进去的蒟蒻先把这题题解写了。
这是把我送进水名的题目。也是我切过的 kenkoooo 分最高的 AtCoder 题。
首先考虑 一定在 前面的做法。
因为加入的数不重,所以一定可以记录每一个数的前驱后继。每一个数都只会被删除一次,所以一定是 的。
回到原问题,需要记录 和 的相对顺序。一个重大结论在于:删数不影响不被删的数的相对顺序。此算法需要离线。
先忽略掉所有的删除操作。把数全部加进去,看数在链表的哪一个位置,得到 和 的相对顺序,然后转化成 在 前的情况。
时间复杂度 。
CPP#include<bits/stdc++.h>
#define int long long
#define mp(a,b) make_pair(a,b)
using namespace std;
int q;
int nxt[5*114514],lst[5*114514];
int place[5*114514]; //代表不删除序列的元素顺序!
int op[5*114514];
int x[5*114514],y[5*114514],z[5*114514];
signed main(){
cin>>q;
nxt[0]=q+1,lst[q+1]=0; //代表节点
for(int i=1;i<=q;i++){
cin>>op[i];
if(op[i]==1){
cin>>x[i];
int lst1=x[i],nxt1=nxt[x[i]]; //代表未来节点的位置
nxt[lst1]=i,lst[i]=lst1;
lst[nxt1]=i,nxt[i]=nxt1; //代表现在的位置
}
else{
cin>>y[i]>>z[i];
}
int now=0;
// cout<<'!';
// while(now!=q+1){
// cout<<now<<' ';
// now=nxt[now];
// }
// cout<<endl;
}
int now=0;
place[now]=1;
int ptr=1;
while(now!=q+1){
now=nxt[now];
ptr++;
place[now]=ptr;
// cout<<now<<' ';
}
// for(int i=0;i<=q;i++){
// cout<<'#'<<i<<' '<<place[i]<<endl;
// }
// cout<<endl;
for(int i=0;i<=q;i++){
nxt[i]=lst[i]=0; //更新位置! 离线做法的精髓!
}
nxt[0]=q+1,lst[q+1]=0;
for(int i=1;i<=q;i++){
if(op[i]==1){
int lst1=x[i],nxt1=nxt[x[i]]; //代表未来节点的位置
nxt[lst1]=i,lst[i]=lst1;
lst[nxt1]=i,nxt[i]=nxt1; //代表现在的位置
}
else{
int st=y[i],ed=z[i];
// cout<<st<<' '<<ed<<endl;
// cout<<place[st]<<' '<<place
if(place[st]>place[ed]) swap(st,ed); //精髓!
int now=nxt[st];
int include13=0;
// cout<<st<<' '<<ed<<endl;
while(now!=ed){
int nxt1=nxt[now],lst1=lst[now];
nxt[lst1]=nxt1,lst[nxt1]=lst1;
include13+=now;
nxt[now]=lst[now]=0;
now=nxt1; //代表目前的情况
}
cout<<include13<<endl;
}
int now=0;
// cout<<"不朽!"<<' ';
// while(now!=q+1){
// cout<<now<<' ';
// now=nxt[now];
// }
// cout<<endl;
}
cout<<endl;
return 0;
} //一定要挽回罗琪钧的遗憾啊,一定会挽回罗琪钧的遗憾的!
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...