CF1217F
强制在线是诈骗,还是很有意思的。
首先,如果没有强制在线就是一个 SGT 分治板子。强制在线看起来做不了,但是发现
容易发现,如果当前时刻
现在就要考虑怎么判断某条边是否被操作,发现这由进行加边操作前的某一次询问决定,而线段树分治最后处理答案刚好是满足在时间轴上从前往后进行的,所以可以在每次递归到叶子时分两种情况:
- 该时间进行了加/删边操作。
此时已经知道
- 查询。
求出实际查询的两点之后查询答案,记录新的
每次在某个节点上面有加边操作就判断当前边是否存在,然后操作即可。
一个小细节是
code:
点击查看代码
int n,m,top,st[N];
pii a[N];
bool lans,pd[N],ans[N];
map<pii,int> lst,vis;
mt19937 rnd(time(0));
struct SGT{
int tot,head[N<<2];
struct node{pii x;int nxt;}e[N<<5];
il void add(int o,pii k){e[++tot]={k,head[o]},head[o]=tot;}
struct BCJ{
int fa[N],c[N];
void init(){rep(i,1,n)c[i]=rnd(),fa[i]=i;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
il void merge(int x,int y){
x=find(x),y=find(y);
if(c[x]>c[y])swap(x,y);
fa[x]=y,st[++top]=x;
}
il void del(int x){while(top>x)fa[st[top]]=st[top],top--;}
}S;
void update(int l,int r,int o,int x,int y,pii k){
if(x>y)return;
if(l>=x&&r<=y)return add(o,k);
int mid=(l+r)>>1;
if(x<=mid)update(l,mid,o<<1,x,y,k);
if(y>mid)update(mid+1,r,o<<1|1,x,y,k);
}
void solve(int l,int r,int o){
int npos=top;
go(i,o){
pii x=e[i].x;
if(vis[x])S.merge(x.fi,x.se);
}
if(l==r){
int u=a[l].fi,v=a[l].se;
if(lans)u=u%n+1,v=v%n+1;
if(u>v)swap(u,v);
if(pd[l]){
if(S.find(u)==S.find(v))ans[l]=lans=1;
else ans[l]=lans=0;
}else vis[Mp(u,v)]^=1;
return S.del(npos);
}
int mid=(l+r)>>1;
solve(l,mid,o<<1),solve(mid+1,r,o<<1|1);
S.del(npos);
}
}T;
void Yorushika(){
scanf("%d%d",&n,&m);
rep(i,1,m){
int op,u,v;scanf("%d%d%d",&op,&u,&v);
if(u>v)swap(u,v);
a[i]=Mp(u,v);
if(op==1){
if(lst[Mp(u,v)])T.update(1,m,1,lst[Mp(u,v)]+1,i-1,Mp(u,v));
lst[Mp(u,v)]=i;
u=u%n+1,v=v%n+1;
if(u>v)swap(u,v);
if(lst[Mp(u,v)])T.update(1,m,1,lst[Mp(u,v)]+1,i-1,Mp(u,v));
lst[Mp(u,v)]=i;
}else pd[i]=1;
}
for(auto i:lst)T.update(1,m,1,i.se+1,m,i.fi);
T.S.init(),T.solve(1,m,1);
rep(i,1,m)if(pd[i])pc(ans[i]+'0');
}
signed main(){
int t=1;
// scanf("%d",&t);
while(t--)
Yorushika();
}