目录

  • @description - translation@
  • @solution@
    • @part - 1@
    • @part - 2@
    • @part - 3@
  • @accepted code@
  • @details@


@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。

@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\n", ans);print();
}

@details@

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

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/10176721.html

@codeforces - 786E@ ALT相关推荐

  1. VSCODE codeforces 插件

    VSCODE codeforces 插件 选择插件 CodePal Codeforces Bot Codeforeces (没错,有个插件就叫这个名字) Catalyst.ICIE.acmX 最后 选 ...

  2. CodeForces 375D Tree and Queries

    传送门:https://codeforces.com/problemset/problem/375/D 题意: 给你一颗有根树,树上每个节点都有其对应的颜色,有m次询问,每次问你以点v为父节点的子树内 ...

  3. 「日常训练」Bad Luck Island(Codeforces Round 301 Div.2 D)

    题意与分析(CodeForces 540D) 是一道概率dp题. 不过我没把它当dp做... 我就是凭着概率的直觉写的,还好这题不算难. 这题的重点在于考虑概率:他们喜相逢的概率是多少?考虑超几何分布 ...

  4. 【codeforces 812C】Sagheer and Nubian Market

    [题目链接]:http://codeforces.com/contest/812/problem/C [题意] 给你n个物品; 你可以选购k个物品;则 每个物品有一个基础价值; 然后还有一个附加价值; ...

  5. CodeForces 获得数据

    针对程序的输出可以看见 CodeForces :当输入.输出超过一定字符,会隐藏内容 所以:分若干个程序进行输入数据的获取 1. 1 for (i=1;i<=q;i++) 2 { 3 scanf ...

  6. codeforces水题100道 第二十七题 Codeforces Round #172 (Div. 2) A. Word Capitalization (strings)...

    题目链接:http://www.codeforces.com/problemset/problem/281/A 题意:将一个英文字母的首字母变成大写,然后输出. C++代码: #include < ...

  7. CodeForces 595A

    题目链接: http://codeforces.com/problemset/problem/595/A 题意: 一栋楼,有n层,每层有m户,每户有2个窗户,问这栋楼还有多少户没有睡觉(只要一个窗户灯 ...

  8. codeforces A. Jeff and Digits 解题报告

    题目链接:http://codeforces.com/problemset/problem/352/A 题目意思:给定一个只有0或5组成的序列,你要重新编排这个序列(当然你可以不取尽这些数字),使得这 ...

  9. Codeforces Round #506 (Div. 3)

    Codeforces Round #506 (Div. 3) 实习期间事不多,对div3 面向题解和数据编程了一波 A. Many Equal Substrings 题目链接 A题就是找后缀和前缀重合 ...

最新文章

  1. 微信是个坑货4-网页授权
  2. Linux中crontab定时执行python程序
  3. 关于安防与大数据不能不说的九个发展趋势
  4. 云存储巧解三大存储难题
  5. Redis数据结构:字典(hash表)
  6. (转)百度文库浏览器分析及实现(续)
  7. uva 11383(二分图最大权匹配)
  8. listview 每行后面的小箭头_主卧带小衣帽装修,这几个装修方案,你喜欢哪个?...
  9. 10分钟带你学会微信小程序的反编译
  10. linux 管理工具 scrt,Linux服务器管理之终端管理软件(SecureCRT)介绍
  11. linux-新建一个centos虚拟机系统-安装全过程图示
  12. centOS 7下安装与配置heartbeat高可用集群
  13. Redis基本操作——队列 List(原理篇)
  14. jenkins没有参数化构建过程选项
  15. 昨天遇到的几个常用函数
  16. NUC980开源项目9-官方Kernel编译
  17. 模式化窗口问题![window.dialogArguments]
  18. mybatis文档地址
  19. python爬app西瓜视频_Python爬虫工程师面试题,采集头条西瓜视频
  20. linux下如何用gcc编译器生成lst文件?

热门文章

  1. Gentoo Framebuffer, Bootsplash Grubsplash 指南(修改版)
  2. 使用CSDN-markdown编辑器笔记
  3. Promise方法汇总
  4. python 批量导入mysql_Python批量插入数据到Mysql
  5. spring boot文件的上传与下载
  6. 35岁的程序员如果不转行,从事哪些细分行业比较好?
  7. 盘口的买一是卖股票还是买股票?
  8. 如果女孩子一辈子不结婚,那会是怎样的人生?
  9. 怎样提高自己的Java编程能力?
  10. 教你如何判断水泥的质量好坏?