1 条题解

  • 0
    @ 2025-8-24 22:49:09

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Melting_Pot
    just slow down

    搬运于2025-08-24 22:49:09,当前版本为作者最后更新于2023-08-22 11:46:24,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目链接:[JOISC 2022 Day1] 监狱

    本题的思路并不刁钻,但十分考验代码能力,因此本蒟蒻尽量讲的仔细一点,尽量串联起思路与代码中的重点,当然也方便本人加深理解。

    • Analysis:

    首先对于两个的罪犯,我们思考他们在什么情况下不合法,无非以下几种:

    1. 两个囚犯路径有重合,且相向而行。

    2. 两个囚犯路径呈包含与被包含关系。

    3. 两个囚犯路径有重合,但处理不当,使其在中途相遇。

      \dots

    我们发现这些限制条件只能提供一些类似于特殊判断的思路,而且限制三肉眼可见的不合理,那么我们只好换一种思路:从最后合法的方案入手。如果最后方案合法,就意味着有一种合理的先后顺序可以安排所有罪犯,使其不相遇且到达终点。

    而囚犯们当然可以先安排一个囚犯走完他的路径,再同理安排另一个,这是因为每个囚犯的移动路径是独立的,要想判断最终情况是否合法,这要知道先后顺序,这与囚犯们谁先走完路径是无关的,故这种移动策略是合理的。

    所以,我们去思考每个囚犯移动的先后顺序,不难得出以下两条简洁规整的性质:

    • 如果 AA 的起点在 BB 的路径上,那么 AA 必须先于 BB 走。
    • 如果 AA 的终点在 BB 的路径上,那么 BB 必须先于 AA 走。

    由此答案也就呼之欲出了,我们根据这两条性质为各个囚犯连有向边表示他们的先后关系,暴力跳点,拓扑判环,如果成环就是不合法方案,那么你会获得一个 Θ(n2)\Theta(n^2) 的连边建图,这样的复杂度在本题的数据范围下当然会被卡的十分拉跨,那么我们将思路优化一下,用线段树优化建图

    我们首先将原图的树复制两棵 S,TS,T,约定用 SxS_xTxT_x 表示原图中点 xx 在这两棵树上的编号(原树的 dfs 序)。

    对于 ii 的路径:

    • 我们从所有路径上的点在 SS 上对应的点向 pip_i 连边,代表起点在这些点上的人必须先于 ii 走,为了传递限制,还需对每个 pip_iSsiS_{s_i} (第 ii 个罪犯的起点的编号)连边
    • 我们从 pip_i 向所有路径上的点在 TT 上对应的点连边,代表 ii 必须先于终点在这些点上的人走,为了传递限制,还需对每个 TtiT_{t_i} (第 ii 个罪犯的终点的编号)向 pip_i 连边。

    如果你还不是很明白,那我们不妨画个图辅助理解:

    首先,我们先看非法的一组数据:

    8
    1 2
    2 4
    4 9
    2 5
    3 6
    3 7
    3 8
    1 3
    2
    4 3
    1 2
    
    

    图长这样:

    因为两条路径有重复,根据上文提到的性质,我们给两个人分别连边,发现成环,因此不合法。但是我们将其放在线段树上,又该怎么办?

    首先按照线段树优化建图的套路,对原树按照 dfs 序建立两棵线段树,一颗为全是出边的”出树“,另一棵为全是入边的“入树”,按照上文提到的连边规则,我们不难连出这样一幅图:

    这是一棵“入树”,可以发现经过连边,P2P_2 罪犯可以向 P1P_1 罪犯连一条有向边,代表 P2P_2P1P_1 的先后顺序,同理,我们再建一棵”出树“(这里不再给出图片),可以发现 P1P_1 罪犯又向 P2P_2 罪犯连出了另一条有向边,我们惊喜的发现:P1P_1P2P_2 罪犯成环了!矛盾的出现意味着非法的诞生,那么我们愉快地将当前局面判为“非法”。

    • Achieve:

    首先树剖求出 dfs 序及重链,然后线段树建出两棵树表示”出入树“,我们发现操作涉及区间对单点加边以及单点对区间加边,那么我们在跳重链的时候对两棵树进行操作,然后就是将单点对单点加边,最后拓扑判环,拜拜程序。

    • Attention:

    1. 两棵线段树实际上建的是同一个图的不同种边(废话)。
    2. 多测的清空记得精细化处理。
    3. 原树与线段树建图一定要区分清楚,不论是思路上还是代码上。
    • Code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5e6+5;
    int n,m;
    int deg[N];
    int to[N<<1],nxt[N<<1],head[N<<1],tot1;
    int to1[N<<1],head1[N<<1],nxt1[N<<1],tot;
    void add1(int u,int v){
        to1[++tot]=v,nxt1[tot]=head1[u],head1[u]=tot;
        to1[++tot]=u,nxt1[tot]=head1[v],head1[v]=tot;
    }
    void add(int u,int v){
        // if(typ) swap(u,v);
        to[++tot]=v,nxt[tot]=head[u],head[u]=tot;
        deg[v]++;
    }
    int f[N],son[N],rev[N],top[N],dfn[N],siz[N],dep[N],cntd;
    void dfs(int u,int fa){
        dep[u]=dep[fa]+1,siz[u]=1,f[u]=fa;
        for(int i=head1[u];i;i=nxt1[i]){
            if(to1[i]==fa) continue;
            dfs(to1[i],u);
            siz[u]+=siz[to1[i]];
            if(siz[to1[i]]>siz[son[u]]) son[u]=to1[i];
        }
    }
    void dfs2(int u,int tp){
        top[rev[dfn[u]=++cntd]=u]=tp;
        if(son[u]) dfs2(son[u],tp);
        for(int i=head1[u];i;i=nxt1[i]) if(to1[i]^f[u]&&to1[i]^son[u]) dfs2(to1[i],to1[i]);
    }
    int cnt(0);
    int leaf[N][2];
    struct SMT{
        #define lc t[pos].ls
        #define rc t[pos].rs
        #define mid ((l+r)>>1)
        struct Node{
            int ls,rs;
        }t[N<<2];
        void build(int &pos,int l,int r,int typ){
            pos=++cnt;
            if(l==r) return leaf[l][typ]=pos,void();
            t[pos].ls=t[pos].rs=0;
            build(lc,l,mid,typ);build(rc,mid+1,r,typ);
            if(typ) add(pos,lc),add(pos,rc);
            else add(lc,pos),add(rc,pos);
        }
        void update(int pos,int l,int r,int L,int R,int x,int typ){
            if(r<L||R<l||R<L) return;
            if(L<=l&&r<=R) return typ?add(x,pos):add(pos,x),void();
            update(lc,l,mid,L,R,x,typ);
            update(rc,mid+1,r,L,R,x,typ);
        }
        #undef lc
        #undef rc
        #undef mid
    }S,T;
    int trt,srt;
    void update(int x,int y,int z){
        if(dep[x]<dep[y]) swap(x,y);
        if(dfn[y]<=dfn[x]&&dfn[x]<=dfn[y]+siz[y]-1){
            x=f[x];
            while(top[x]^top[y]){
                S.update(srt,1,n,dfn[top[x]],dfn[x],z,0);
                T.update(trt,1,n,dfn[top[x]],dfn[x],z,1);
                x=f[top[x]];
            }
            S.update(srt,1,n,dfn[y]+1,dfn[x],z,0);
            T.update(trt,1,n,dfn[y]+1,dfn[x],z,1);
        }else{
            x=f[x],y=f[y];
            while(top[x]^top[y]){
                if(dep[top[x]]<dep[top[y]]) swap(x,y);
                S.update(srt,1,n,dfn[top[x]],dfn[x],z,0);
                T.update(trt,1,n,dfn[top[x]],dfn[x],z,1);
                x=f[top[x]];
            }
            if(dfn[x]<dfn[y]) swap(x,y);
            S.update(srt,1,n,dfn[y],dfn[x],z,0);
            T.update(trt,1,n,dfn[y],dfn[x],z,1);
        }
    }
    bool topo(){
        queue<int> q;
        for(int i=1;i<=cnt;i++) if(!deg[i]) q.push(i);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=nxt[i]){
                deg[to[i]]--;
                if(!deg[to[i]]) q.push(to[i]);
            }
        }
        for(int i=1;i<=cnt;i++) if(deg[i]) return false;
        return true;
    }
    void clear(){
        memset(head1,0,sizeof(int)*(n+1));
        memset(head,0,sizeof(int)*(cnt+1));
        memset(son,0,sizeof(int)*(n+1));
        memset(deg,0,sizeof(int)*(cnt+1));
        tot=tot1=cnt=cntd=srt=trt=0;
    }
    int main(){
        //  freopen("prison.in","r",stdin);
        //  freopen("prison.out","w",stdout);
        int t;cin>>t;
        while(t-->0){
            clear();
            scanf("%d",&n);
            for(int i=2,u,v;i<=n;i++){
                scanf("%d%d",&u,&v);
                add1(u,v);
            }
            dfs(1,0),dfs2(1,1);
            S.build(srt,1,n,0);
            T.build(trt,1,n,1);
            scanf("%d",&m);
            for(int i=1,s,t;i<=m;i++){
                scanf("%d%d",&s,&t);
                cnt++;
                add(leaf[dfn[t]][0],cnt),add(cnt,leaf[dfn[s]][0]);
                add(leaf[dfn[t]][1],cnt),add(cnt,leaf[dfn[s]][1]);
                update(s,t,cnt);
            }
            puts(topo()?"Yes":"No");
        }
    }
    

    感谢阅读!!!

    • 1

    信息

    ID
    9051
    时间
    4000ms
    内存
    1024MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者