bzoj4464
2016-05-20 15:50
323 查看
OJ上放的题解是网络流这里先放一份(然而是过不了的,血泪教训,40w个点,上百万条边,跑毛啊.)这里还是放一份代码.
写完网络流爆炸之后我发现艹这不是sb贪心吗?我们考虑这样贪心,对于每个点i,从它的儿子记录有多少条到它的链,有多少条到它儿子的链,分别设为A,B,然后我们发现1个A可以抵掉1个B,为什么在这里抵掉是最优的呢?因为就算一条这样的链现在不抵掉,就算之后抵掉了其它的链,那么和当前的贡献一样,抵不掉其它链反而更亏,所以说不抵白不抵,因为可以这样抵,所以说其实每个点只用记录两个信息f[x],g[x]表示有多少条链,链的方向就可以了.注意转移的时候如果儿子节点g[son]不等于当前边的方向的话,那么f[son]直接算到答案中去,同时x的A(B)只对应加1,否则就加f[son].注意当f[son]=0的时候要特判一下.每次把min(A,B)加到答案中再把剩下的链数记录在f[x]中,最后答案加上f[1].复杂度o(n).mdzz网络流.
#include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cstdio> #include <algorithm> #include <cmath> #include <ctime> #define inf 1000000000 using namespace std; struct node {int to;int next;int len;}; node bian[3000010],edge[500010]; int size = 1,first[400010],tot = 0,fir[400010],p[400010],cnt = 0; int N,s,t,dis[400010],stack[3000010],n,a,b; long long cost; bool visit[400010],exist[400010]; void inser(int x,int y,int z) { size ++; bian[size].to = y; bian[size].next = first[x]; first[x] = size; bian[size].len = z; } void add(int x,int y) { tot ++; edge[tot].to = y; edge[tot].next = fir[x]; fir[x] = tot; } void dfs(int x,int Anc) { visit[x] = true; if(p[x] == 0) p[x] = ++ cnt; for(int u = fir[x];u;u = edge[u].next) { if(edge[u].to == Anc) continue; cnt = cnt + 1; inser(p[x],cnt + N,inf); inser(cnt + N,p[x],0); inser(p[x] + N,cnt + N,inf); inser(cnt + N,p[x] + N,0); if(visit[edge[u].to] == true) { inser(cnt,p[edge[u].to] + N,inf); inser(p[edge[u].to] + N,cnt,0); inser(cnt + N,p[edge[u].to] + N,inf); inser(p[edge[u].to] + N,cnt + N,0); } else { p[edge[u].to] = ++ cnt; inser(cnt - 1,p[edge[u].to] + N,inf); inser(p[edge[u].to] + N,cnt - 1,0); inser(cnt + N - 1,p[edge[u].to] + N,inf); inser(p[edge[u].to] + N,cnt + N - 1,0); dfs(edge[u].to,x); } } } int Dfs(int x,int F) { if(x == t) return F; int ret = 0; for(int u = first[x];u && F;u = bian[u].next) if(dis[bian[u].to] == dis[x] + 1 && bian[u].len > 0) { int d = Dfs(bian[u].to,min(F,bian[u].len)); F -= d; ret += d; bian[u].len -= d; bian[u ^ 1].len += d; } if(ret == 0 || F == 0) dis[x] = -5; return ret; } bool spfa(int x,int y) { int head = 0,tail = 1; for(int i = 0;i <= t;i ++) dis[i] = inf; stack[1] = x;dis[x] = 0; while(head != tail) { head = head + 1; if(head == 1000000) head = 1; int k = stack[head]; exist[k] = false; for(int u = first[k];u;u = bian[u].next) { if(dis[bian[u].to] == inf && bian[u].len > 0) { dis[bian[u].to] = dis[k] + 1; tail ++; if(tail == 1000000) tail = 1; stack[tail] = bian[u].to; exist[bian[u].to] = true; } } } return dis[y] <= 1000000; } int maxflow(int x,int y) { int ret = 0,tim = 0; while(spfa(x,y)) tim ++,ret += Dfs(s,inf); return ret; } int main() { scanf("%d",&n); for(int i = 2;i <= n;i ++) { scanf("%d",&a); a = a + 1; scanf("%d",&b); b = b + 1; add(a,b); } N = 2 * n - 1; memset(visit,false,sizeof(visit)); for(int i = 1;i <= n;i ++) if(visit[i] == false) dfs(i,0); s = 0,t = N * 2 + 1; for(int i = 1;i <= N;i ++) { inser(s,i,1); inser(i,s,0); } for(int i = N + 1;i <= 2 * N;i ++) { inser(i,t,1); inser(t,i,0); } N = N - maxflow(s,t); printf("%d",N); }
写完网络流爆炸之后我发现艹这不是sb贪心吗?我们考虑这样贪心,对于每个点i,从它的儿子记录有多少条到它的链,有多少条到它儿子的链,分别设为A,B,然后我们发现1个A可以抵掉1个B,为什么在这里抵掉是最优的呢?因为就算一条这样的链现在不抵掉,就算之后抵掉了其它的链,那么和当前的贡献一样,抵不掉其它链反而更亏,所以说不抵白不抵,因为可以这样抵,所以说其实每个点只用记录两个信息f[x],g[x]表示有多少条链,链的方向就可以了.注意转移的时候如果儿子节点g[son]不等于当前边的方向的话,那么f[son]直接算到答案中去,同时x的A(B)只对应加1,否则就加f[son].注意当f[son]=0的时候要特判一下.每次把min(A,B)加到答案中再把剩下的链数记录在f[x]中,最后答案加上f[1].复杂度o(n).mdzz网络流.
#include <iostream> #include <cstring> #include <cstdlib> #include <string> #include <cstdio> #include <algorithm> #include <cmath> #include <ctime> using namespace std; struct node {int to;int next;int flag;};node bian[200010]; int size = 0,first[200010],f[200010],g[200010],n,a,b,Ans; void inser(int x,int y,int z) { size ++; bian[size].to = y; bian[size].next = first[x]; bian[size].flag = z; first[x] = size; } void dfs(int x,int Anc) { int A = 0,B = 0; for(int u = first[x];u;u = bian[u].next) { if(bian[u].to == Anc) continue; dfs(bian[u].to,x); if(bian[u].flag == 1) { if(f[bian[u].to]) { if(g[bian[u].to]) { A += f[bian[u].to]; } else Ans += f[bian[u].to],A ++; } else A ++; } else { if(f[bian[u].to]) { if(!g[bian[u].to]) { B += f[bian[u].to]; } else Ans += f[bian[u].to],B ++; } else B ++; } } int delta = min(A,B); Ans += delta; A -= delta; B -= delta; if(A > 0) { f[x] = A; g[x] = 1; } else if(B > 0) { f[x] = B; g[x] = 0; } else f[x] = 0,g[x] = 1; } int main() { scanf("%d",&n); for(int i = 2;i <= n;i ++) { scanf("%d%d",&a,&b); a = a + 1; b = b + 1; inser(a,b,1); inser(b,a,0); } dfs(1,1); cout<<Ans + f[1]; }
相关文章推荐
- 对偶问题小解释
- android的多渠道打包
- shell中 if else以及大于、小于、等于逻辑表达式介绍
- 283MoveZeroes
- TF-IDF与余弦相似性的应用(二):自动摘要
- AndroidManifest配置之uses-permission
- 高质量的UI组件下载地址暂存
- Android中用到UI控件
- scala actor异步
- 一个无锁消息队列引发的血案(三)——地:q3.h 与 RingBuffer
- BZOJ 4552: [Tjoi2016&Heoi2016]排序
- RabbitMQ基础概念详细介绍
- storybord 中设置collectionView中cell的背景色无效
- Ubuntu安装adobe字体
- Every derived table must have its own alias
- mongodb启动
- 《梦断代码》读后笔记_4
- IE四个接口互相获取
- 实例讲解Android中的AutoCompleteTextView自动补全组件
- Proto-gen-lua 与 C# 对 Extension 的不同处理