JZOJ 6231 【NOI2019模拟2019.6.25】等你哈苏德(欧拉回路,最大流)

Description


Joker 有一些黑. 白. 区. 间. [li; ri],有些区间已经被指定了颜色,有些却没有。你要指定
这些未染色区间的颜色,使得数轴上对于每个点,覆盖他的黑区间个数和白区间个数差
的绝对值小. 于. 等. 于. 1

1<=m<=3e4

题解:


首先这一类问题可以往网络流啊欧拉回路这一类图论算法上靠。

假设把每一个点看成一个小段,即把区间[l,r]变成[l,r+1],使l->r+1连边,如果所有点的度数都是偶数,找若干条欧拉回路来给边定向,这个时候对每一段来看,由于是回路,走到另外一边一定会走回来,所以黑区间-白区间=0

那么如果有度数奇数的点怎么办呢?
不妨在这些奇点中加辅助边,不妨从左往右扫,当前的点是奇点,就和下一个点连边(不是下一个奇点)。

由于一个段这样最多被一条辅助边覆盖,所以就是abs<=1的。

然后考虑有一些边被定向了怎么办。

对没有定向的边也定个向,那么这些边可以反向。

对每一点求出入度-出度。

负数就放二分图的左边,正数就放二分图的右边,流量是abs,如果有一个边x->y可以反向,那么就连这条边,流过这条边的意义就显然了。

注意还是要加辅助边(可反)。

有解条件满流,一条边被流过表示反向。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i <  B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;
map<int, int> bz;

int n, m, d[N], d0;
int l[N], r[N], w[N], b[N];
int fi[N], nt[N * 10], to[N * 10], v[N * 10], tot = 1;

void link(int x, int y, int z) {
	nt[++ tot] = fi[x], to[tot] = y, v[tot] = z, fi[x] = tot;
	nt[++ tot] = fi[y], to[tot] = x, v[tot] = 0, fi[y] = tot;
}
int S, T, co[N], cc[N], cur[N];

int dg(int x, int flow) {
	if(x == T) return flow;
	int use = 0;
	for(int i = cur[x]; i; cur[x] = i = nt[i])
		if(cc[x] == cc[to[i]] + 1 && v[i]) {
			int t = dg(to[i], min(flow - use, v[i]));
			v[i] -= t; v[i ^ 1] += t; use += t;
			if(flow == use) return use;
		}
	cur[x] = fi[x];
	if(!(-- co[cc[x]])) cc[S] = T;
	++ co[++ cc[x]];
	return use;
}

int main() {
	freopen("wait.in", "r", stdin);
	freopen("wait.out", "w", stdout);
	scanf("%d %d", &n, &m);
	fo(i, 1, n) {
		scanf("%d %d %d", &l[i], &r[i], &w[i]);
		r[i] ++;
		d[++ d0] = l[i]; d[++ d0] = r[i];
	}
	sort(d + 1, d + d0 + 1);
	d0 = unique(d + 1, d + d0 + 1) - (d + 1);
	fo(i, 1, d0) bz[d[i]] = i;
	fo(i, 1, d0) d[i] = 0;
	S = d0 + 1, T = S + 1;
	fo(i, 1, n) {
		l[i] = bz[l[i]];
		r[i] = bz[r[i]];
		if(w[i] == -1) {
			d[l[i]] --; d[r[i]] ++;
			link(l[i], r[i], 2);
			b[i] = tot;
		} else
		if(w[i] == 0) d[l[i]] --, d[r[i]] ++; else
		d[l[i]] ++, d[r[i]] --;
	}
	fo(i, 1, d0) if(d[i] & 1) {
		d[i] --;
		d[i + 1] ++;
		link(i, i + 1, 2);
	}
	int ans = 0;
	fo(i, 1, d0) if(d[i] <= 0)
		link(S, i, -d[i]); else
		link(i, T, d[i]), ans += d[i];
	co[0] = T;
	for(; cc[S] < T; ) ans -= dg(S, 1 << 30);
	if(!ans) {
		fo(i, 1, n) {
			if(w[i] != -1) pp("%d ", w[i]); else
			pp("%d ", v[b[i]] == 2);
		}
	} else {
		pp("-1\n");
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值