Codeforces 1217 F
如果不强制在线,这题如何解决?
类似之前模拟赛的一题,我们注意到每条边的存在组成了一个区间。那么考虑使用线段树维护。
具体的,我们加入一个区间的时候,我们知道它在时刻加入,在时刻删除,那么我们在线段树上找到对应的区间,使用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);
}
}
那么如何查询两个点是否联通?考虑使用并查集,但直接加入边是的。不妨利用上一次的并查集,那么我们需要删掉一些边,再加入一些边。看起来要可持久化并查集?其实不需要:因为删掉的边一定是最后加入的边(更靠近线段树的叶子),那么我们就可以按秩合并,删边时还原数组,这样时的时间复杂度。
下面是只按秩合并的程序。
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);
}
}
}
}
但是此题还要强制在线。
不过,我们发现,这个强制在线是假的。因为只能是或。这样处理到第个操作,且是加边操作,那么找到后面第一个“可疑”的时刻(就是在加或加后等于这个询问的边),更新到那么“可疑”的时刻。在计算到那个时刻时再看看此时的第个操作加入的边是否删除,如果不是,那么就寻找下一个“可疑”的点即可。
由于造成“可疑”的点只能由或得到,时间复杂度还是。下面是全部代码:
#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;
}
标签:
Data Structure
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话