【Codeforces 1007D】Ants

树剖线段树分治前缀优化建图 2-SAT。

题目大意:

给定一棵 $n$ 个点的树,有 $m$ 只蚂蚁,第 $i$ 只蚂蚁有参赛 $a_i,b_i,c_i,d_i$。

现在,每只蚂蚁可以选择是从 $a_i$ 到 $b_i$ 还是从 $c_i$ 到 $d_i$。

要求输出一种蚂蚁的选择方案,使得一条路径只被一只蚂蚁经过。

题解:

二选一的问题,容易想到 2-SAT。

直接连边是 $O(m^2)$ 的,无法接受,考虑优化连边。

对树进行树链剖分,然后在 dfs 序上进行线段树分治。

对每条路径,将其拆成 $O(\log^2n)$ 条 dfs 序上的连续段。

考虑递归遍历这棵线段树,并用栈保存当前的路径。

一条路径入栈的时候,栈中的其它路径和它,只能选择 $1$ 条。

这就可以用到一个比较经典的优化建图方法了。每个点入栈的时候,新增一对点,表示这条路径及其之前的路径中,是否已经选了一条了。

由于这是一个栈,所以每次入栈的路径对应的前一个点是唯一的。

然后我们就有 $O(m\log^2 n)$ 个点和边。用 Tarjan 求解 2-SAT 即可。

时空复杂度 $O(n+m\log^2 n)$。

Code:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=114514,M=8000005;
struct edge{
    int to,nxt;
}e[N<<1];
int head[N],fa[N],son[N],cnt,n,dfn[N],top[N],idx,sz[N],dep[N],idfn[N],m;
struct info{
    int u1,v1,u2,v2;
}A[N];
namespace Graph{
    edge e[M];
    int nod,dfn[M],idx,head[M],cnt,low[M],sta[M],SCC,bel[M],top;
    bool vis[M];
    inline void addedge(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}
    void dfs(int now){
        vis[now]=1,sta[++top]=now;
        dfn[now]=low[now]=++idx;
        for(int i=head[now];i;i=e[i].nxt)
        if(!dfn[e[i].to])dfs(e[i].to),low[now]=min(low[now],low[e[i].to]);else
        if(vis[e[i].to])low[now]=min(low[now],dfn[e[i].to]);
        if(low[now]==dfn[now]){
            ++SCC;
            int v;
            do{
                v=sta[top--];
                bel[v]=SCC;
                vis[v]=0;
            }while(v!=now);
        }
    }
    void work(){
        for(int i=1;i<=nod;++i)if(!dfn[i])dfs(i);
        for(int i=1;i<=m;++i)if(bel[i]==bel[i+m])return void(puts("NO"));
        for(int i=m*2+1;i<=nod;i+=2)if(bel[i]==bel[i+1])return void(puts("NO"));
        puts("YES");
        for(int i=1;i<=m;++i)puts(bel[i]<bel[i+m]?"1":"2");
    }
}
void dfs(int now){
    sz[now]=1;
    for(int i=head[now];i;i=e[i].nxt)
    if(!dep[e[i].to]){
        dep[e[i].to]=dep[now]+1,fa[e[i].to]=now,dfs(e[i].to);
        sz[now]+=sz[e[i].to];
        if(!son[now]||sz[son[now]]<sz[e[i].to])son[now]=e[i].to;
    }
}
void dfs2(int now){
    idfn[dfn[now]=++idx]=now;
    if(son[now])top[son[now]]=top[now],dfs2(son[now]);
    for(int i=head[now];i;i=e[i].nxt)
    if(e[i].to!=son[now]&&dep[e[i].to]>dep[now])dfs2(top[e[i].to]=e[i].to);
}
vector<int>vec[N<<2];
void add_(int l,int r,int o,const int&L,const int&R,const int&v){
    if(L>R)return;
    if(L<=l&&r<=R)vec[o].push_back(v);else{
        const int mid=l+r>>1;
        if(L<=mid)add_(l,mid,o<<1,L,R,v);
        if(mid<R)add_(mid+1,r,o<<1|1,L,R,v);
    }
}
inline int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
void add(int x,int y,int id){
    int t=LCA(x,y);
    bool b=1;
    deb:
    while(top[x]!=top[t]){
        add_(1,n,1,dfn[top[x]],dfn[x],id);
        x=top[x];
        if(fa[x]==t)break;
        x=fa[x];
    }
    if(top[x]==top[t]&&x!=t)add_(1,n,1,dfn[t]+1,dfn[x],id);
    if(b){x=y,b=0;goto deb;}
}
pair<int,int>sta[N];int tp;
void solve(int l,int r,int o){
    int TOP=tp;
    for(int id:vec[o]){
        pair<int,int>&nw=sta[tp+1];
        nw.first=++Graph::nod,nw.second=++Graph::nod;
        int _1=id,_0=id>m?id-m:id+m;
        if(tp){
            Graph::addedge(sta[tp].first,nw.first),
            Graph::addedge(nw.second,sta[tp].second);
            Graph::addedge(sta[tp].first,_0);
            Graph::addedge(_1,sta[tp].second);
        }
        Graph::addedge(_1,nw.first),Graph::addedge(nw.second,_0);
        ++tp;
    }
    if(l<r){
        const int mid=l+r>>1;
        solve(l,mid,o<<1),solve(mid+1,r,o<<1|1);
    }
    tp=TOP;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        e[++cnt]=(edge){v,head[u]},head[u]=cnt;
        e[++cnt]=(edge){u,head[v]},head[v]=cnt;
    }
    dfs(dep[1]=1),dfs2(top[1]=1);
    scanf("%d",&m);
    Graph::nod=m*2;
    for(int i=1;i<=m;++i){
        scanf("%d%d%d%d",&A[i].u1,&A[i].v1,&A[i].u2,&A[i].v2);
        add(A[i].u1,A[i].v1,i);add(A[i].u2,A[i].v2,i+m);
    }
    solve(1,n,1);
    Graph::work();
    return 0;
}