1. 程式人生 > >@codeforces - [email protected] ALT

@codeforces - [email protected] ALT

目錄


@description - [email protected]

給定一棵含 n 個點的樹和 m 個人,第 i 個人會從結點 xi 走到 結點 yi。
每個人有一個需求:要麼他開局自帶一條狗,要麼他走的路徑上

是狗。
你可以給某一個人一隻狗,也可以在某一條邊上放一隻狗。
求滿足所有人需求狗的最少數量。輸出方案
可愛的狗狗~

input
第一行包含兩個整數 n 和 m,表示點的數量與人的數量。
接下來 n-1 行每行兩個整數 u v,描述了樹上的一條邊。
接下來 m 行每行兩個整數 xi yi,描述了每個人要走的路徑。

output
第一行輸出最少的狗數。
第二行先輸出帶狗的人數,然後輸出一種相應的方案。
第二行先輸出有狗的邊數,然後輸出一種相應的方案。

sample input
4 5
2 4
3 4
1 4
2 4
2 1
2 4
1 2
2 3
sample output
3
1 5
2 3 1
sample explain


樣例對應的樹:
樣例圖

@[email protected]

一道建模較簡單,建圖令人想要**的網路流題。

@part - [email protected]

如果熟悉這種型別的題的話,很容易想到這其實是個最小割模型。
人向源點連容量為 1 的邊,樹上的每條邊向匯點連容量為 1 的邊,然後人向他經過的樹邊連容量為 inf 的邊。
因為 S 到 T 的每條路徑都被最小割所截,而容量為 inf 的邊又不可能會被割。所以要麼人到源點的邊被割,要麼樹邊到匯點的邊被割。所以求出來的最小割一定滿足題意。

@part - [email protected]

那麼問題就來了。
這 TM 網路流邊數是 O(N^2) 的。
好的,我們有線段樹優化網路流建圖的方法,那我們就線段樹上樹,變成樹鏈剖分。
好的那我們就解決這個問題了。

具體到實現,我們把邊的資訊下放到點去。每個點儲存它連向它父親的邊。
然後爬重鏈的時候,注意重鏈的頂端其實儲存的是它連向它父親的那條輕邊。
然後結點 1 的資訊是沒有意義的,但是你建圖是絕對不會連結點 1 的。

一條路徑包含 log n 條重鏈,每條重鏈拆成 log n 條線段樹上的線段。共 O(nlog^2 n) 條邊。

如果你不知道線段樹怎麼優化建圖,很抱歉我真的不想寫那玩意兒【因為我懶 www】。
如果你不會樹鏈剖分……我建議你還是別來寫這道題了 qwq。

@part - [email protected]

好的。這道題還要輸出方案。

注意到最小割的邊一定滿流。
但是滿流的邊不一定在最小割中。
所以我們不能直接取滿流的邊作為方案。

我們從源點開始 dfs,不經過那些滿流的邊。
標記訪問到的點為 S 部,沒訪問到的點為 T 部。
則所有 S 到 T 的邊都是割邊。
正確性顯然。

根據上面那個東西,我們可以知道:
如果一個人在 T 部,則他帶了狗。
如果一條樹邊在 S 部,則它有狗。

機房裡的 dalao 不知道為什麼就卡在了輸出方案這一步。

@accepted [email protected]

我沒有封裝網路流……因為我真的太懶了 qwq。

#include<cstdio>
#include<vector>
#include<iostream> 
#include<algorithm>
using namespace std;
const int MAXN = 20000;
const int MAXM = 10000;
const int MAXV = 200000;
const int MAXE = 10000000;
const int INF = (1<<30);
vector<pair<int, int> >G[MAXN + 5];
void add_edge(int u, int v, int n) {
    G[u].push_back(make_pair(v, n));
    G[v].push_back(make_pair(u, n));
}
int siz[MAXN + 5], hvy[MAXN + 5], dep[MAXN + 5], fa[MAXN + 5], num[MAXN + 5];
int top[MAXN + 5], tid[MAXN + 5], dfn[MAXN + 5], dcnt = 0;
void dfs1(int rt, int pre) {
    siz[rt] = 1, hvy[rt] = 0, dep[rt] = dep[pre] + 1, fa[rt] = pre;
    for(int i=0;i<G[rt].size();i++) {
        int to = G[rt][i].first;
        if( to == pre ) continue;
        num[to] = G[rt][i].second;
        dfs1(to, rt); siz[rt] += siz[to];
        if( siz[to] > siz[hvy[rt]] )
            hvy[rt] = to;
    }
}
int vcnt = 0, S, T;
struct SegmentTree{
    int le, ri, num;
}tree[4*MAXN + 5];
void build_segtree(int x, int l, int r) {
    tree[x].le = l, tree[x].ri = r, tree[x].num = (++vcnt);
    if( l == r ) return ;
    int mid = (l + r) >> 1;
    build_segtree(x<<1, l, mid);
    build_segtree(x<<1|1, mid+1, r);
}
void dfs2(int rt, int tp) {
    top[rt] = tp, dfn[++dcnt] = rt, tid[rt] = dcnt;
    if( !hvy[rt] ) return ;
    dfs2(hvy[rt], tp);
    for(int i=0;i<G[rt].size();i++) {
        int to = G[rt][i].first;
        if( to != fa[rt] && to != hvy[rt] )
            dfs2(to, to);
    }
}
struct edge{
    int to, cap, flow;
    edge *nxt, *rev;
}edges[MAXE + 5], *adj[MAXV + 5], *ecnt=&edges[0];
void addedge(int u, int v, int c) {
    edge *p = (++ecnt);
    p->to = v, p->cap = c, p->flow = 0;
    p->nxt = adj[u], adj[u] = p;
    edge *q = (++ecnt);
    q->to = u, q->cap = 0, q->flow = 0;
    q->nxt = adj[v], adj[v] = q;
    p->rev = q, q->rev = p;
}
int pos[MAXV + 5];
void build_edge_segtree(int x) {
    if( tree[x].le == tree[x].ri ) {
        addedge(tree[x].num, T, 1);
        pos[tree[x].num] = dfn[tree[x].le];
    }
    else  {
        addedge(tree[x].num, tree[x<<1].num, INF); build_edge_segtree(x<<1);
        addedge(tree[x].num, tree[x<<1|1].num, INF); build_edge_segtree(x<<1|1);
    }
}
void build_edge_human_segtree(int x, int l, int r, int h) {
    if( l <= tree[x].le && tree[x].ri <= r ) {
        addedge(h, tree[x].num, INF);
        return ;
    }
    if( l > tree[x].ri || r < tree[x].le )
        return ;
    build_edge_human_segtree(x<<1, l, r, h);
    build_edge_human_segtree(x<<1|1, l, r, h);
}
void build_edge_human_tree(int h, int u, int v) {
    while( top[u] != top[v] ) {
        if( dep[top[u]] < dep[top[v]] ) swap(u, v);
        build_edge_human_segtree(1, tid[top[u]], tid[u], h);
        u = fa[top[u]];
    }
    if( dep[u] < dep[v] ) swap(u, v);
    build_edge_human_segtree(1, tid[v]+1, tid[u], h);
}
int d[MAXV + 5], vd[MAXV + 5];
int aug(int x, int tot) {
    if( x == T ) return tot;
    int mind = T+1, sum = 0;
    for(edge *p=adj[x];p!=NULL;p=p->nxt) {
        if( p->cap > p->flow ) {
            if( d[p->to] + 1 == d[x] ) {
                int del = aug(p->to, min(tot - sum, p->cap - p->flow));
                p->flow += del, p->rev->flow -= del, sum += del;
                if( d[S] >= T+1 ) return sum;
                if( sum == tot ) return sum;
            }
            mind = min(mind, d[p->to]);
        }
    }
    if( sum == 0 ) {
        vd[d[x]]--;
        if( !vd[d[x]] )
            d[S] = T+1;
        d[x] = mind + 1;
        vd[d[x]]++;
    }
    return sum;
}
int sap() {
    int flow = 0;
    while( d[S] < T+1 )
        flow += aug(S, INF);
    return flow;
}
bool vis[MAXV + 5];
void dfs3(int x) {
    vis[x] = true;
    for(edge *p=adj[x];p!=NULL;p=p->nxt)
        if( !vis[p->to] && p->cap > p->flow )
            dfs3(p->to);
}
vector<int>dog, human; 
void print() {
    dfs3(S);
    for(edge *p=adj[S];p!=NULL;p=p->nxt)
        if( !vis[p->to] ) human.push_back(p->to);
    printf("%d", human.size());
    for(int i=0;i<human.size();i++)
        printf(" %d", human[i]-vcnt);
    puts("");
    for(edge *p=adj[T];p!=NULL;p=p->nxt)
        if( vis[p->to] ) dog.push_back(p->to);
    printf("%d", dog.size());
    for(int i=0;i<dog.size();i++)
        printf(" %d", num[pos[dog[i]]]);
    puts("");
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1;i<n;i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add_edge(u, v, i);
    }
    dfs1(1, 0); dfs2(1, 1);
    build_segtree(1, 1, n);
    T = vcnt + m + 1;
    build_edge_segtree(1);
    for(int i=1;i<=m;i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        addedge(S, vcnt+i, 1);
        build_edge_human_tree(vcnt+i, x, y);
    }
    int ans = sap();
    printf("%d\n", ans);
    print();
}

@[email protected]

禁止養苟。
tid 和 dfn 的含義還是傻傻不分。
一開始是沒有 pos 這個陣列的,是 num 陣列反覆用,然後就玩出問題來了。