hdu 5452 Minimum Cut(树链剖分+差分前缀和)
2015-09-22 20:01
381 查看
题意:
给一个无向图和它的一个生成树,要求找到一个最小割,使得有且只有一条生成树上的一条边属于割集。解析:
因为生成树中只有一条边属于割集,那么割对生成树来说只是分成了两个子树,那么就考虑割生成树上割哪条边是最优的。首先对生成树进行建树剖,对于每条非树边的两个端点u和v,对 u –> v 在生成树上的简单路径上的边权值加一,最后找到所有边权值最小的边,就是属于最小割的边。
所有对找到的这条边的权值做贡献的边,一定是跨越了以这条边分开的两个子树,即如果要分开这两个子树,这些边也要割掉,这些边就是要求的最小割集。
这题卡常数,所以路径上边权加一的操作不能用线段树或者树状数组来更新,因为是操作完成后才对边权进行遍历,所以可以用差分前缀和来计算每条边被更新了几次。
mymy codecode
[code]#include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <vector> #define pb push_back using namespace std; const int INF = 0x3f3f3f3f; const int N = 20005; const int M = 200005; namespace IO { const static int maxn = 200 << 18; static char buf[maxn], *pbuf = buf, *End; void init() { int c = fread(buf, 1, maxn, stdin); End = buf + c; } int &readint() { static int ans; ans = 0; while (pbuf != End && !isdigit(*pbuf)) pbuf ++; while (pbuf != End && isdigit(*pbuf)) { ans = ans * 10 + *pbuf - '0'; pbuf ++; } return ans; } } struct Query { int u, v; } Q[M]; vector<int> G ; int fa , son , size , top , deep ; int p , fp , dfs_clock; void dfs1(int u, int pre, int de) { deep[u] = de, fa[u] = pre, size[u] = 1; son[u] = 0; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (v == pre) continue; dfs1(v, u, de + 1); size[u] += size[v]; if (size[v] > size[son[u]]) son[u] = v; } } void dfs2(int u, int tp) { top[u] = tp; p[u] = ++dfs_clock; fp[dfs_clock] = u; if(son[u]) dfs2(son[u], tp); for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (v != fa[u] && v != son[u]) dfs2(v, v); } } void init_chain(int u) { dfs_clock = 0; dfs1(u, 0, 1); dfs2(u, u); } int n, m, q; int sum ; inline void modify(int ql, int qr, int val) { sum[ql] += val; sum[qr+1] -= val; } void build() { for(int i = 1; i <= n; i++) sum[i] += sum[i-1]; } void update(int u, int v, int val) { while (top[u] != top[v]) { if (deep[top[u]] < deep[top[v]]) swap(u, v); modify(p[top[u]], p[u], val); u = fa[top[u]]; } if (deep[u] < deep[v]) swap(u, v); modify(p[v], p[u], val); } inline void addEdge(int u, int v) { G[u].pb(v); } void init() { q = 0; memset(sum, 0, sizeof(sum)); for(int i = 1; i <= n; i++) G[i].clear(); } int main() { IO::init(); int u, v; int T, cas = 1; T = IO::readint(); while(T--) { init(); n = IO::readint(); m = IO::readint(); for(int i = 1; i <= m; i++) { u = IO::readint(); v = IO::readint(); if(i < n) { addEdge(u, v); addEdge(v, u); }else Q[q++] = (Query){u, v}; } init_chain(1); for(int i = 0; i < q; i++) { u = Q[i].u, v = Q[i].v; update(u, v, 1); } build(); int ans = INF; for(int i = 2; i <= n; i++) ans = min(ans, sum[i]+1); printf("Case #%d: %d\n", cas++, ans); } return 0; }
相关文章推荐
- Yii中的save方法
- LightOJ 1030 Discovering Gold 概率
- Android读写操作之内存的读写操作
- 《机器学习》__note2
- Swift 风格指南
- iOS开发 ----- UITableView
- 在功能上,坚果云是处于领先地位的
- poj2142-The Balance(扩展欧几里德算法)
- C#中多态问题
- 关于北京找工作的点滴记叙
- 黑马程序员——Swift学习笔记:声明与基本类型
- 黑马程序员——Swift学习笔记:声明与基本类型
- 返回的文件名不带扩展名
- POJ 3349 Snowflake Snow Snowflakes ( HASH+最小表示判同构 )
- Windows Performance Toolkit
- ZOJ 3328 Searching the String (AC自动机)
- SpringMVC设置缺省的返回数据格式
- 【作业二】结对项目之需求分析与原型模型设计
- 使用PL/SQL删除百万条记录的大表
- C语言程序初体验-第六课-第一题:两个正整数的正差值