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

这题其实很典,转差分。关键点就是抓清楚一次优秀的操作一定是从有多余球的盒子开始,然后到需要球的盒子结束,中间怎么流经我其实是不用管的。

posted @ 2025-09-05 12:17  FxorG  阅读(45)  评论(0)    收藏  举报