CF786E ALT

题意

有一棵 \(n\) 个点的数,和 \(m\) 个人,每个人的路径是树上的一条链。
每个人都喜欢巴啦啦小魔仙。你需要给出一些魔法棒(给人或给树上节点)。
每个人希望要么自己得到魔法棒,要么自己的路径每个点都有魔法棒。
问满足每个人要求的最小魔法棒数,需要输出方案。

题解

我们发现这个题并不可以用数据结构做,也没有奇妙的算法。

考虑暴力一点,网络流。(\(n=2\times10^4\) 不是问题,我甚至在 \(\tt 550ms\) 内跑过 \(n=2\times10^5\) 的问题)。

我们思考建边,用流量代表魔法棒,然后是一个最小割(为了方便,我仅把边权分成 \(1\)\(\infty\) 两种)。

  1. 对于每个人,从源点连向他,流量为 \(1\)(这是给人的选择)。
  2. 对于每个人,从他连向他的路径上的每个点,流量为 \(\infty\)(这是给节点的选择)。

考虑最小割的过程:

  • 要么选择割掉 1 类边,这样这个人本身已经分割完毕
  • 要么选择割掉 2 类边,这时需要所有的 2 类边都被割开才能完成分割。

所以最小割,即最大流就是答案。

输出方案:在跑完最大流的网络上 \(\tt dfs\)


完了?

发现最多有 \(n^2\) 条边。即使 \(\tt dinic\) 复杂度能承受,边数也承受不了。

但是这是 一个点向一堆连续的点连边,考虑线段树优化。(有一说一这场的 \(\tt B\) 是建图优化,还是板)

但是这是树链不是区间,所以需要树剖,然后再线段树。

总边数 \(n\log^2n\),问题不大。


事实是以前的最大流板子有锅,然后浪费了 \(\texttt{1h+}\) 去调。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>

using namespace std;

#define rep(i,a,b) for(int i = (a);i <= (b);++i)

const int N = 2e4 + 5;
const int S = N * 5 + 5;
const int inf = 1e9;

int n,m,st,ed,res;
int id[N << 2];
vector<pair<int,int>> G[N];
int dep[N],siz[N],son[N],fa[N],dfn[N],adfn[N],num[N],tot = 0,top[N];

namespace Dinic{
    struct edge{ int v,w; edge *nxt,*inv; bool flg; } *from[S];

    void adde(int u,int v,int w){
        edge *E = from[u] = new(edge){v,w,from[u],nullptr,1};
        edge *F = from[v] = new(edge){u,0,from[v],nullptr,0};
        E->inv = F; F->inv = E;
    }

    queue<int> q;
    int dep[S];
    edge *now[S];
    bool vis[S];

    int level(){
        while(!q.empty()) q.pop();
        q.push(st);
        memset(dep,0,sizeof dep);
        memcpy(now,from,sizeof now);
        dep[st] = 1;
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(edge *e = from[u];e;e = e->nxt){
                int v = e->v,w = e->w;
                if(w && !dep[v]){
                    dep[v] = dep[u] + 1;
                    q.push(v);
                }
            }
        }
        return dep[ed];
    }

    int dfs(int u,int fl){
        if(u == ed) return res += fl,fl;
        int cur = fl;
        for(edge *e = now[u];e && cur;e = e->nxt){
            now[u] = e;
            int v = e->v,w = e->w;
            if(w && dep[v] == dep[u] + 1){
                int cfl = dfs(v,w < cur ? w : cur);
                e->w -= cfl; e->inv->w += cfl; cur -= cfl;
            }
        }
        return fl - cur;
    }

    void solve(){ while(level()) dfs(st,inf); }
    
    void fnd(int u){
        vis[u] = true;
        for(edge *e = from[u];e;e = e->nxt){
            int v = e->v,w = e->w;
            if(w && !vis[v]) fnd(v);
        }
    }

    void output(){
        fnd(st);
        int cnt0 = 0,cnt1 = 0;
        for(edge *e = from[st];e;e = e->nxt)
            if(!vis[e->v]) ++cnt0;
        printf("%d ",cnt0);
        for(edge *e = from[st];e;e = e->nxt)
            if(!vis[e->v]) printf("%d ",e->v - 4 * n);
        for(edge *e = from[ed];e;e = e->nxt)
            if(vis[e->v]) ++cnt1;
        printf("\n%d ",cnt1);
        for(edge *e = from[ed];e;e = e->nxt)
            if(vis[e->v]) printf("%d ",num[adfn[id[e->v]]]);
    }
} using Dinic::adde;

#define lc (i << 1)
#define rc (i << 1 | 1)
#define mid ((L + R) >> 1)

#define ls lc,L,mid
#define rs rc,mid + 1,R
#define ID int i = 1,int L = 1,int R = n

void build(ID){
    if(L == R) return id[i] = L,adde(i,ed,1);
    adde(i,lc,inf); adde(i,rc,inf);
    build(ls); build(rs);
}

void link(int k,int l,int r,ID){
    if(l <= L && R <= r) return adde(k,i,inf);
    if(l <= mid) link(k,l,r,ls);
	if(r > mid) link(k,l,r,rs);
}

void dfs1(int u = 1,int ft = 0){
    dep[u] = dep[fa[u] = ft] + 1;
    siz[u] = 1;
    for(auto[v,i] : G[u]) if(v != ft){
        num[v] = i;
        dfs1(v,u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u = 1,int tp = 1){
    adfn[dfn[u] = ++tot] = u; top[u] = tp;
    if(son[u]) dfs2(son[u],tp);
    for(auto[v,i] : G[u]) if(!top[v]) dfs2(v,v);
}

void lnk(int k,int x,int y){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x,y);
        link(k,dfn[top[x]],dfn[x]);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    if(x != y) link(k,dfn[x] + 1,dfn[y]);
}

int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,n - 1){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].emplace_back(v,i);
        G[v].emplace_back(u,i);
    }
    dfs1(); dfs2();
    st = 4 * n + m + 1,ed = 4 * n + m + 2;
    build();
    for(int i = 1;i <= m;++i){
        int x,y,I = 4 * n + i;
        scanf("%d%d",&x,&y);
        lnk(I,x,y);
        adde(st,I,1);
    }
    Dinic::solve();
    printf("%d\n",res);
    Dinic::output();
    return 0;
}
posted @ 2022-08-18 21:55  One_Zzz  阅读(43)  评论(0)    收藏  举报