城市重建(网络流)

题意一开始不是很好理解,然后考试的时候就把它跳过了。。

题意:有M年,N个城市,每年最多修建K个城市。然后会发生一系列的事件,包括拆一条边,加一条边,对x所在的连通块中的城市进行重修。每个城市最多重修一次,求最多有多少城市可以被重修,然后输出每一个发生重修事件的年份实际修了多少城市,要求字典序最小。N,M都在几百左右。

其实就是按着他的步骤模拟一下,如果遇到可修的年份,就向连通块里面全部连上边。但是要注意,同一个年份可以被用K次,所以如果用匈牙利匹配的话需要将一个年份拆成K个,然后倒序匹配。如果用网络流的话,就不需要拆点了,直接把流量上调就行了,但是网络流不好控制字典序,一个比较暴力的做法就是强行给边上设费用,让编号小的边费用更大。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define LL long long
#define clr(a) memset(a,0,sizeof a)
const int inf = 0x3f3f3f3f;
const int MAXN = 1005;
const int MAXM = 500000;

int N, M, K;

struct Ed {
	int to, cap, cost;
	Ed*nxt, *back;
};
struct FlowNet
{
	Ed Edge[MAXM], *ecnt, *adj[MAXN];
	FlowNet () { ecnt=Edge; }
	int dis[MAXN];
	bool vis[MAXN];
	int vn, S, T, tot, flow;
	inline void adde(int a, int b, int c, int d)
	{
		(++ecnt)->to = b;
		ecnt->cap = c;
		ecnt->cost = d;
		ecnt->nxt = adj[a];
		ecnt->back = ecnt+1;
		adj[a] = ecnt;
		(++ecnt)->to = a;
		ecnt->cap = 0;
		ecnt->cost = -d;
		ecnt->nxt = adj[b];
		ecnt->back = ecnt-1;
		adj[b] = ecnt;
	}
	void init(int n, int s, int t)
	{
		tot = flow = 0;
		vn = n; S = s; T = t;
		clr(dis), clr(adj);
		ecnt = Edge;
	}
	bool update()
	{
		int tmp = inf;
		for (int i = 1; i<=vn; ++i)
			if (vis[i]) for (Ed *p = adj[i]; p; p=p->nxt)
				if (p->cap > 0 && !vis[p->to])
					tmp = min(tmp, dis[p->to]-dis[i]+p->cost);
		if (tmp == inf) return 0;
		for (int i = 1; i<=vn; ++i)
			if (vis[i]) dis[i] += tmp;
		return 1;
	}
	int aug(int u, int augco)
	{
		if (u == T)
		{
			tot += dis[S] * augco;
			flow += augco;
			return augco;
		}
		vis[u] = 1;
		int delta, augc = augco;
		for (Ed*p = adj[u]; p && augc; p=p->nxt)
		{
			int&v = p->to;
			if (!vis[v] && p->cap && dis[u]==dis[v]+p->cost)
			{
				delta = aug(v, min(p->cap, augc));
				p->cap -= delta, p->back->cap += delta;
				augc -= delta;
			}
		}
		return augco - augc;
	}
	int mcmf()
	{
		do {
			do memset(vis, 0, sizeof vis); while (aug(S, inf));
		} while (update());
		return tot;
	}
	void calc(int *ans, int yeah)
	{
		for (Ed*p = adj[S]; p; p=p->nxt)
			if (p->to > 0 && p->to <= yeah)
				ans[p->to] = p->back->cap;
	}
	void debug()
	{
		for (Ed*p = adj[T]; p; p=p->nxt)
			if (p->cap) printf("city %d:%d\n", p->to-M, p->cap);
	}
} G;

bool way[MAXN][MAXN];
int scc[MAXN], cnt, yeah;
bool vis[MAXN];
int ans[MAXN];

void dfs(int u)
{
	vis[u] = 1, scc[++cnt] = u;
	rep(v, 1, N) if (way[u][v] && !vis[v]) dfs(v);
}

int main()
{
	int cas, op, x, y, duang;
	scanf("%d", &cas);
	while (cas --)
	{
		scanf("%d%d%d", &N, &M, &K);
		G.init(M+N+2, M+N+1, M+N+2);
		clr(way);
		yeah = 0;
		rep (id, 1, M)
		{
			scanf("%d", &op);
			switch (op)
			{
				case 1:
				G.adde(G.S, ++yeah, K, M-id);
				scanf("%d", &x);
				clr(vis), cnt = 0, dfs(x);
				rep(i, 1, cnt) G.adde(yeah, M+scc[i], 1, 0);
				break;

				case 2:
				scanf("%d%d", &x, &y);
				way[x][y] = way[y][x] = 1;
				break;

				default:
				scanf("%d", &duang);
				while (duang--)
					scanf("%d%d", &x, &y), way[x][y] = way[y][x] = 0;
			}
		}
		rep(i, 1, N) G.adde(M+i, G.T, 1, 0);
		G.mcmf();
		printf("%d\n", G.flow);
		G.calc(ans, yeah);
		rep(i, 1, yeah)
			printf("%d%c", ans[i], i==yeah?'\n':' ');
		//G.debug();
	}
	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值