Codeforces 1217F Forced Online Queries Problem

题意

n个点的图,一开始是空图,两种操作,一种是增减 ( u , v ) (u,v) (u,v),若之前无边,则加上该边,若之前有边,则去掉该边,另一种是提问两个点,看是否联通,强制在线。

题解

先考虑不强制在线的解法
定期重构,每次求解一个块内的答案
首先,将块之前的边且最后留下来的边分为两种
一是在块内没有出现的边
二是在块内出现的边
对于第一种边,直接建立即可,而且后面也不会被删去
在建完第一种边之后,紧接着建立第二种边,他们在后面更改

遍历块内的边
若为加边,直接加
若为减边,则回滚到增加该边的操作,跳过该边,将回滚时删去的边再加入,就完成了删边的操作

根据建图的特殊顺序,第二种最多有 m \sqrt m m 种,所以每次回滚为 O ( m ) O(\sqrt m) O(m )
并查集的复杂度为 l o g ( n ) log(n) log(n)
所以总复杂度为 m m   l o g ( n ) m\sqrt m ~log(n) mm  log(n)

考虑强制在线,题目中的强制在线的方法,只有两种情况 ( u , v ) 或 ( u + 1 , v + 1 ) (u,v)或(u+1,v+1) (u,v)(u+1,v+1)
所以,这两种边都存下
有影响的只有块前的边建图的时候,无非是第二类的边多了一点,发现对复杂度没有影响

实际测试,发现,块的大小, m \sqrt m m 不是最优, 20 m 20\sqrt m 20m 左右是最优的
不难理解,每个块求解一次, O ( n ) O(n) O(n)的遍历是不可少的,但在增减边的时候,减边期望只有不到1/3,而且回滚时复杂度跑不慢,所以可以增加块的大小,增加回滚时间,减少块的个数

代码
#include<bits/stdc++.h>
#define N 400010
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std; 
typedef pair<int,LL> pp;
int n,m,cnt,ans,tot;
unordered_map<LL,int> id;
int op[N],d[N],fa[N],rk[N],lg[N],hs[N];
struct node{
    int u,v; LL h;
    void bb(){if (u>v) swap(u,v);h=(LL) u<<30|v; }
}e[N],g[N];
int getfa(int x){ return fa[x]==x?x:getfa(fa[x]); }

void uni(int x,int y,int z){
    x=getfa(x),y=getfa(y);
    if (rk[x]<=rk[y]){
        fa[x]=y; lg[++tot]=x; hs[tot]=z;
        if (rk[x]==rk[y]) rk[y]++;
    }else{
        fa[y]=x; lg[++tot]=y; hs[tot]=z;
    }
}
 
void del(int x){
    int i;
    for(i=tot;hs[i]!=x;i--) fa[lg[i]]=lg[i];  fa[lg[i]]=lg[i];
    int u=tot; tot=i-1;
    for(i++;i<=u;i++) uni(g[hs[i]].u,g[hs[i]].v,hs[i]);
}
 
void solve(int be,int en){
    for(int i=1;i<=n;i++) fa[i]=i,rk[i]=0; tot=0;
    vector<int> v(cnt+3),vv(cnt+3);
    for(int i=1;i<be;i++) if (op[i]==1) v[d[i]]^=1; 
    for(int i=be;i<=en;i++) if (op[i]==1) vv[d[i]]=vv[d[i+m]]=1; 
    for(int i=1;i<=cnt;i++) if (v[i]&&!vv[i]) uni(g[i].u,g[i].v,i);
    for(int i=1;i<=cnt;i++) if (v[i]&&vv[i]) uni(g[i].u,g[i].v,i);
    for(int i=be;i<=en;i++){
        if (ans) e[i]=e[i+m],d[i]=d[i+m];
        if (op[i]==1){
            v[d[i]]^=1;
            if (v[d[i]]) uni(e[i].u,e[i].v,d[i]); else del(d[i]); 
        }else{  
            int x=getfa(e[i].u),y=getfa(e[i].v);
            if (x!=y) putchar('0'),ans=0;else putchar('1'),ans=1;
        }
    }
}
 
int main(int argc, char const *argv[])
{
    scc(n,m);
    for(int i=1;i<=m;i++){
        sc(op[i]); scc(e[i].u,e[i].v); e[i].bb();
        op[i+m]=op[i]; e[i+m]={e[i].u%n+1,e[i].v%n+1}; e[i+m].bb();
    }
    for(int i=1,tot=m*2;i<=tot;i++){
        LL t=e[i].h;
        if (!id[t]) id[t]=++cnt,g[cnt]=e[i];
        d[i]=id[t];
    }
    int B=sqrt(m)*30; B=min(B,m);
    for(int i=1;i<=m;i+=B) solve(i,min(i+B-1,m));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值