• @codeforces



    @description - translation@

    给定一棵含 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
    样例对应的树:
    样例图

    @solution@

    一道建模较简单,建图令人想要**的网络流题。

    @part - 1@

    如果熟悉这种类型的题的话,很容易想到这其实是个最小割模型。
    人向源点连容量为 1 的边,树上的每条边向汇点连容量为 1 的边,然后人向他经过的树边连容量为 inf 的边。
    因为 S 到 T 的每条路径都被最小割所截,而容量为 inf 的边又不可能会被割。所以要么人到源点的边被割,要么树边到汇点的边被割。所以求出来的最小割一定满足题意。

    @part - 2@

    那么问题就来了。
    这 TM 网络流边数是 O(N^2) 的。
    好的,我们有线段树优化网络流建图的方法,那我们就线段树上树,变成树链剖分。
    好的那我们就解决这个问题了。

    具体到实现,我们把边的信息下放到点去。每个点存储它连向它父亲的边。
    然后爬重链的时候,注意重链的顶端其实存储的是它连向它父亲的那条轻边。
    然后结点 1 的信息是没有意义的,但是你建图是绝对不会连结点 1 的。

    一条路径包含 log n 条重链,每条重链拆成 log n 条线段树上的线段。共 O(nlog^2 n) 条边。

    如果你不知道线段树怎么优化建图,很抱歉我真的不想写那玩意儿【因为我懒 www】。
    如果你不会树链剖分……我建议你还是别来写这道题了 qwq。

    update in 2020/01/21:可以用 ST 表优化边数到 O(n),但是并不能优化点数(orz 栋爷)

    @part - 3@

    好的。这道题还要输出方案。

    注意到最小割的边一定满流。
    但是满流的边不一定在最小割中。
    所以我们不能直接取满流的边作为方案。

    我们从源点开始 dfs,不经过那些满流的边。
    标记访问到的点为 S 部,没访问到的点为 T 部。
    则所有 S 到 T 的边都是割边。
    正确性显然。

    根据上面那个东西,我们可以知道:
    如果一个人在 T 部,则他带了狗。
    如果一条树边在 S 部,则它有狗。

    机房里的 dalao 不知道为什么就卡在了输出方案这一步。

    @accepted code@

    我没有封装网络流……因为我真的太懒了 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
    ", ans);
    	print();
    }
    

    @details@

    禁止养苟。
    tid 和 dfn 的含义还是傻傻不分。
    一开始是没有 pos 这个数组的,是 num 数组反复用,然后就玩出问题来了。
    还有爬重链的时候并不能保证是直接从链底爬到链顶的,它也可以从中间爬到链顶嘛。

  • 相关阅读:
    图像,script,link 空地址带来的困惑
    localStorage变更事件当前页响应新解-awen
    opencv在ios上的开发教程
    torch-ios框架XCODE使用备忘
    重拾老本行_图像处理
    dialogic d300语音卡驱动重装后启动报错问题解决方法
    Winpcap构建用户级网桥
    winpcap usb山寨网卡识别
    最近这段时间
    银行IT入门深似海
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10176721.html
Copyright © 2020-2023  润新知