bzoj4464

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

#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];
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值