Codeforces 1217 F

如果不强制在线,这题如何解决?

类似之前模拟赛的一题,我们注意到每条边的存在组成了一个区间。那么考虑使用线段树维护。

具体的,我们加入一个区间的时候,我们知道它在l时刻加入,在r时刻删除,那么我们在线段树上找到对应的区间,使用vector存储。

//seg[pos]表示当前线段树对应的区间
void update(int l,int r,int from,int to,int pos) {
	if(l<=seg[pos].first&&seg[pos].second<=r) {
		edge[pos].push_back({from,to});
	} else if(seg[pos].first<=r&&seg[pos].second>=l) {
		update(l,r,from,to,pos<<1);
		update(l,r,from,to,pos<<1|1);
	}
}

那么如何查询两个点是否联通?考虑使用并查集,但直接加入边是O(n2)的。不妨利用上一次的并查集,那么我们需要删掉一些边,再加入一些边。看起来要可持久化并查集?其实不需要:因为删掉的边一定是最后加入的边(更靠近线段树的叶子),那么我们就可以按秩合并,删边时还原fa数组,这样时O(nlogn)的时间复杂度。
下面是只按秩合并的程序。

int fa[maxn];
std::stack<std::array<int,3>> st;
 
int root(int pp) {
	return fa[pp]<0?pp:root(fa[pp]);
}
 
void merge(int p1,int p2,int ind) {
	int s1=root(p1),s2=root(p2);
	if(s1==s2) return;
	if(fa[s1]>fa[s2]) std::swap(s1,s2);
	st.push({ind,s1,fa[s1]}); st.push({ind,s2,fa[s2]});
	fa[s1]+=fa[s2]; fa[s2]=s1;
}
 
int query(int p1,int p2) {
	return root(p1)==root(p2);
}

下面是更新并查集的函数:

void move(int target) {
	while(seg[now].first!=target||seg[now].second!=target) {
		if(target<seg[now].first||seg[now].second<target) {
			while(!st.empty()&&st.top()[0]==now) {
				fa[st.top()[1]]=st.top()[2];
				st.pop();
			}
			now>>=1;
		} else {
			now<<=1;
			if(seg[now].second<target) now|=1;
			for(auto par : edge[now]) {
				merge(par.first,par.second,now);
			}
		}
	}
}

但是此题还要强制在线。

不过,我们发现,这个强制在线是假的。因为lastanswer只能是01。这样处理到第i个操作,且是加边操作,那么找到后面第一个“可疑”的时刻(就是在加0或加1后等于这个询问的边),更新到那么“可疑”的时刻。在计算到那个时刻时再看看此时的第i个操作加入的边是否删除,如果不是,那么就寻找下一个“可疑”的点即可。

由于造成“可疑”的点只能由+0+1得到,时间复杂度还是O(nlogn)。下面是全部代码:

#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

const int maxn=200005;
int n,m,last;
int op[maxn],x[maxn],y[maxn];
std::vector<int> quest[maxn];
std::map<std::pair<int,int>,std::vector<int>> vec;

int now=1;
std::pair<int,int> seg[maxn<<2];
std::vector<std::pair<int,int>> edge[maxn<<2];

void build(int pos,int lef,int rig) {
	seg[pos]=std::make_pair(lef,rig);
	if(lef==rig) return;
	int mid=lef+rig>>1;
	build(pos<<1,lef,mid); build(pos<<1|1,mid+1,rig);
}

void update(int l,int r,int from,int to,int pos) {
	if(l<=seg[pos].first&&seg[pos].second<=r) {
		edge[pos].push_back({from,to});
	} else if(seg[pos].first<=r&&seg[pos].second>=l) {
		update(l,r,from,to,pos<<1);
		update(l,r,from,to,pos<<1|1);
	}
}

int fa[maxn];
std::stack<std::array<int,3>> st;

int root(int pp) {
	return fa[pp]<0?pp:root(fa[pp]);
}

void merge(int p1,int p2,int ind) {
	int s1=root(p1),s2=root(p2);
	if(s1==s2) return;
	if(fa[s1]>fa[s2]) std::swap(s1,s2);
	st.push({ind,s1,fa[s1]}); st.push({ind,s2,fa[s2]});
	fa[s1]+=fa[s2]; fa[s2]=s1;
}

int query(int p1,int p2) {
	return root(p1)==root(p2);
}

void move(int target) {
	while(seg[now].first!=target||seg[now].second!=target) {
		if(target<seg[now].first||seg[now].second<target) {
			while(!st.empty()&&st.top()[0]==now) {
				fa[st.top()[1]]=st.top()[2];
				st.pop();
			}
			now>>=1;
		} else {
			now<<=1;
			if(seg[now].second<target) now|=1;
			for(auto par : edge[now]) {
				merge(par.first,par.second,now);
			}
		}
	}
}

int find(int beg,int xx,int yy) {
	int ind=std::upper_bound(vec[{xx,yy}].begin(),vec[{xx,yy}].end(),beg)-vec[{xx,yy}].begin();
	if(ind>=vec[{xx,yy}].size()) return m+1; else return vec[{xx,yy}][ind];	//若没有就返回n+1 
}

int getnext(int beg,int xx,int yy) {
	return std::min(find(beg,xx,yy),find(beg,(xx==1?n:xx-1),(yy==1?n:yy-1)));
}

void expand(int ind,int i) {
	int nxt=getnext(i,x[ind],y[ind]);
	quest[nxt].push_back(ind);
	update(i,nxt-1,x[ind],y[ind],1);
}

int main() {
	memset(fa,-1,sizeof fa);
	scanf("%d%d",&n,&m); build(1,1,m);
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d",&op[i],&x[i],&y[i]);
		if(op[i]==1) {
			vec[{x[i],y[i]}].push_back(i);
			vec[{y[i],x[i]}].push_back(i);
		}
	}
	for(int i=1;i<=m;i++) {
		if(i>1) {
			for(auto par : edge[1]) {
				merge(par.first,par.second,1);
			}
		}
		x[i]=(x[i]+last-1)%n+1,y[i]=(y[i]+last-1)%n+1;
		if(op[i]==1) { int is=1;
			for(auto pos : quest[i]) {
				if(std::min(x[i],y[i])==std::min(x[pos],y[pos])&&std::max(x[i],y[i])==std::max(x[pos],y[pos])) {
					is=0;
				} else {
					expand(pos,i);
				}
			}
			if(is) {
				expand(i,i);
			}
		} else {
			move(i);
			last=query(x[i],y[i]);
			printf("%d",last);
		}
	}
	return 0;
}
posted @   Nastia  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示