bzoj4464

来源:互联网 发布:html写桌面软件 编辑:程序博客网 时间:2024/06/09 17:39

OJ上放的题解是网络流这里先放一份(然而是过不了的,血泪教训,40w个点,上百万条边,跑毛啊.)这里还是放一份代码.

#include <iostream>#include <cstring>#include <cstdlib>#include <string>#include <cstdio>#include <algorithm>#include <cmath>#include <ctime>#define inf 1000000000using 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];}


0 0
原创粉丝点击