跳转至

P11369 弥留之国的爱丽丝

题面

原题链接 click

题解

我常常追忆过去。

注意到这个题不弱于 DAG 可达性问题,考虑除以 \(w\) 的做法。
对询问分块,我们只考虑块内关键点之间的可达性问题。
假设每 \(B\) 个操作一块,我们将这里被修改的边的端点以及询问涉及的两个点视作关键点,共产生 \(2B\) 个关键点。
然后,对于不在区间内被修改的边,我们直接花 \(O((n+m)\frac{w+B}{w})\) 的时间处理关键点可达性。对于会被修改的边,单独维护它们的状态。询问时考虑 \(\mathrm{bitset}\) 优化搜索即可。
\(n,m,q\) 同阶,则时间复杂度大概是 \(O(\frac{n^2}w+\frac{n^2}B+\frac{nB^2}w)\),取 \(B=\Theta(\sqrt n)\) 或者 \(\Theta(w)\) 均可。
我感觉取 \(B=32\) 会比较好,但没试过。

Code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int ans=0;
    char c=getchar();
    while(c<'0'||c>'9'){
//      if(c=='-') f=-f;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        ans=(ans<<3)+(ans<<1)+c-48;
        c=getchar();
    }
    return ans;
}
int n,m,q;
int u[100005],v[100005],col[100005],ky[100005];//col=1为黑,=0为白,是否为关键边
const int B=203;
bitset<B*2+5>g[B*2+5],f[B*2+5];
int key[B*2+5];//关键点->原点
int ku[B+5],kv[B+5],kc[B+5],kid[B+5],num;//关键边
int id[50005];//原点->关键点
int x[100005],y[100005];//操作
int cnt;//关键点数量
vector<int>h1[100005],h2[100005],h3[100005];//原图,反图,缩点后新图的反图
int dfn[100005],now;
bool vis[100005];
int scc[100005],tot;//所在强连通分量编号
bitset<B*2+5>g2[100005];
int in[100005];
void topsort(){
    queue<int>q;
    for(int i=1;i<=tot;i++){
        for(int j=0;j<h3[i].size();j++){
            in[h3[i][j]]++;
        }
    }
    for(int i=1;i<=tot;i++){
        if(!in[i]) q.push(i);
    }
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=0;i<h3[u].size();i++){
            in[h3[u][i]]--;
            g2[h3[u][i]]|=g2[u];
            if(!in[h3[u][i]]) q.push(h3[u][i]);
        }
    }
}
void dfs1(int u){
    vis[u]=1;
    for(int i=0;i<h1[u].size();i++){
        if(!vis[h1[u][i]]) dfs1(h1[u][i]);
    }
    dfn[++now]=u;
}
void dfs2(int u,int k){
    scc[u]=k;
    for(int i=0;i<h2[u].size();i++){
        if(!scc[h2[u][i]]) dfs2(h2[u][i],k);
    }
}
void rev(int k){
    int d=0;
    for(int i=1;i<=num;i++){
        if(kid[i]==k){
            kc[i]^=1;
            d=i;
        }
    }
    g[ku[d]][kv[d]]=f[ku[d]][kv[d]];
//  cout<<d<<' '<<ku[d]<<' '<<kv[d]<<'\n';
//  cout<<g[ku[d]][kv[d]]<<'\n';
    for(int i=1;i<=num;i++){
        if(ku[i]==ku[d]&&kv[i]==kv[d]&&kc[i]) g[ku[i]][kv[i]]=1;
    }
}
bitset<B*2+5>viss;
int qry(int x,int y){
    viss.set();
    queue<int>q;
    q.push(x);
    viss[x]=0;
    while(!q.empty()){
        int u=q.front(); q.pop();
        if(u==y) return 1;
        bitset<B*2+5>tp=viss&g[u];
        viss&=~g[u];
        for(int i=tp._Find_first();i<=cnt;i=tp._Find_next(i)){
            q.push(i);
        }
    }
    return 0;
}
void solve(int l,int r){
//  cout<<"("<<l<<' '<<r<<")\n";
//  cout<<"进行预处理:\n";
    cnt=0;
    for(int i=l;i<=r;i++){
        if(y[i]==0){
            id[u[x[i]]]=1; id[v[x[i]]]=1;
            ky[x[i]]=1;
        }else{
            id[x[i]]=1; id[y[i]]=1;
        }
    }
    for(int i=1;i<=n;i++){
        if(id[i]){
            id[i]=++cnt;
            key[cnt]=i;
        }
    }
//  cout<<"本轮关键点:";
//  for(int i=1;i<=cnt;i++){
//      cout<<key[i]<<' ';
//  }
//  cout<<'\n';
    for(int i=1;i<=m;i++){
        if(!ky[i]&&col[i]){
            h1[u[i]].push_back(v[i]);
            h2[v[i]].push_back(u[i]);
        }
    }
    for(int i=1;i<=n;i++) if(!vis[i]) dfs1(i);
//  cout<<"h1: \n";
//  for(int i=1;i<=n;i++){
//      for(int j=0;j<h1[i].size();j++){
//          cout<<i<<"->"<<h1[i][j]<<'\n';
//      }
//  }
//  cout<<"h2: \n";
//  for(int i=1;i<=n;i++){
//      for(int j=0;j<h2[i].size();j++){
//          cout<<i<<"->"<<h2[i][j]<<'\n';
//      }
//  }
//  cout<<"得到dfn序:";
//  for(int i=1;i<=n;i++) cout<<dfn[i]<<' ';
//  cout<<'\n';
//  memset(vis,0,sizeof(vis));
    for(int i=n;i>=1;i--) if(!scc[dfn[i]]) dfs2(dfn[i],++tot);
//  cout<<"所有节点scc划分结果:\n";
//  for(int i=1;i<=n;i++) cout<<scc[i]<<' '; cout<<'\n';
    for(int i=1;i<=cnt;i++) g2[scc[key[i]]][i]=1;
    for(int i=1;i<=m;i++){
        if(!ky[i]&&col[i]&&scc[u[i]]!=scc[v[i]]){
            h3[scc[v[i]]].push_back(scc[u[i]]);
        }
    }
    topsort();
//  cout<<"所有点到关键点可达性:\n";
//  for(int i=1;i<=n;i++){
//      for(int j=1;j<=cnt;j++){
//          cout<<g2[i][j];
//      }
//      cout<<'\n';
//  }
    for(int i=1;i<=cnt;i++) g[i]=f[i]=g2[scc[key[i]]];
//  cout<<"关键点到关键点可达性:\n";
//  for(int i=1;i<=cnt;i++){
//      for(int j=1;j<=cnt;j++){
//          cout<<f[i][j];
//      }
//      cout<<'\n';
//  }
    for(int i=1;i<=m;i++){
        if(ky[i]&&col[i]) g[id[u[i]]][id[v[i]]]=1;
    }
    for(int i=1;i<=m;i++) if(ky[i]){
        num++;
        ku[num]=id[u[i]];
        kv[num]=id[v[i]];
        kc[num]=col[i];
        kid[num]=i;
    }
    //预处理结束,处理询问
    for(int i=l;i<=r;i++){
        if(!y[i]) rev(x[i]);
        else{
//          cout<<"现在关键点到关键点可达性:\n";
//          for(int i=1;i<=cnt;i++){
//              for(int j=1;j<=cnt;j++){
//                  cout<<g[i][j];
//              }
//              cout<<'\n';
//          }
//          cout<<"处理询问:"<<id[x[i]]<<' '<<id[y[i]]<<'\n';
            int u=qry(id[x[i]],id[y[i]]);
            if(u){
                putchar('Y'); putchar('E'); putchar('S'); putchar(10);
            }
            else{
                putchar('N'); putchar('O'); putchar(10);
            }
        }
    }
    //询问结束,进行更新、清零
    for(int i=l;i<=r;i++) if(!y[i]) col[x[i]]^=1;
    memset(ky,0,sizeof(ky));
    memset(key,0,sizeof(key));
    memset(id,0,sizeof(id));
    memset(dfn,0,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    memset(scc,0,sizeof(scc));
    memset(in,0,sizeof(in));
    memset(ku,0,sizeof(ku));
    memset(kv,0,sizeof(kv));
    memset(kc,0,sizeof(kc));
    memset(kid,0,sizeof(kid));
    num=0;
    for(int i=1;i<=n;i++){
        h1[i].clear(); h2[i].clear(); h3[i].clear();
    }
    cnt=now=tot=0;
    for(int i=1;i<=n;i++) g2[i]=bitset<B*2+5>();
}
int main(){
//  ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//  cin>>n>>m>>q;
    n=read(); m=read(); q=read();
    for(int i=1;i<=m;i++){
        u[i]=read(); v[i]=read(); col[i]=1;
    }
    for(int i=1;i<=q;i++){
        y[i]=read(); x[i]=read();
        if(y[i]==2) y[i]=read();
        else y[i]=0;
    }
//  for(int i=1;i<=q;i++) cout<<x[i]<<'_'<<y[i]<<'\n';
    int beg=1;
    for(int i=1;i<=q;i++){
        if(i%B==0||i==q){
            solve(beg,i);
            beg=i+1;
        }
    }
    return 0;
}