XCPC 训练
DS
F - Erase between X and Y
考虑顺着做不行的原因在于这个序列是动态的,我们没办法在动态的前提下,快速确定某个元素的位置,你考虑链表的话,我们确实可以去记录每个值所在的元素,可是对于 2 的话,你难以判定两个 pos 的先后顺序。
因此,考虑如何定住这个序列,即每个元素在序列中是有确定的位置的,这就需要我们离线了,考虑不理操作 2 的删除,构造到最后的序列出来,这样再顺序做一次,每个元素都有对应的位置了,至于如何快速定位+插入,我们可以用链表+维护每个值在链表的下表即可。
后记:考虑 难以判定两个 pos 的先后顺序 这句话,那我们直接来一个同步跳,即从 x 开始跳 nex,y 也跳 nex,每次各自同时跳一步,看看谁到达另一个即可,这样的复杂度显然是对的,因为我们跳的时候其实也是删掉的时候,而同步跳保证了另一边遍历的复杂度。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N=(int)(5e5+5);
struct node {
int v,nex;
node() {
v=nex=0;
}
}p[N];
struct qu {
int op,x,y;
}op[N];
int q,cnt,pos[N],a[N],tot;
set<int>s;
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>q;
++cnt;
p[1].v=0; pos[0]=1;
for(int i=1;i<=q;i++) {
cin>>op[i].op>>op[i].x;
if(op[i].op==2) cin>>op[i].y;
if(op[i].op==1) {
int qwq=pos[op[i].x];
int prenex=p[qwq].nex;
++cnt;
pos[i]=cnt;
p[cnt].v=i;
p[cnt].nex=prenex;
p[qwq].nex=cnt;
// cout<<"add "<<cnt<<" "<<p[cnt].v<<" "<<p[cnt].nex<<'\n';
}
}
memset(pos,0,sizeof(pos));
for(int x=1;x;x=p[x].nex) {
// cout<<p[x].v<<' ';
a[++tot]=p[x].v;
pos[p[x].v]=tot;
}
// cout<<'\n';
for(int i=1;i<=q;i++) {
int o=op[i].op,x=op[i].x,y=op[i].y;
if(o==2) {
ll ans=0;
int p=pos[x],q=pos[y];
if(p>q) swap(p,q);
auto itl=s.upper_bound(p),itr=s.lower_bound(q);
if(itl!=s.end()) {
for(;itl!=itr;) {
// cout<<"!!! "<<*itl<<'\n';
ans+=a[*itl];
s.erase(itl++);
}
}
cout<<ans<<'\n';
} else {
s.insert(pos[i]);
}
}
return 0;
}
flow
G - Increase to make it Increasing
这题其实很典,转差分。关键点就是抓清楚一次优秀的操作一定是从有多余球的盒子开始,然后到需要球的盒子结束,中间怎么流经我其实是不用管的。

浙公网安备 33010602011771号