SD省队集训2019Day1之“等你哈苏德”

等你哈苏德(wait)

题目描述

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

输入格式

从文件 wait.in 中读入数据。
输入正整数 m, n
接下来 m 行每行三个整数 Li, Ri,wi,保证 1 ≤ Li ≤ Ri ≤ n,wi ∈ [−1, 1]
wi = −1 表示未染色,wi = 0 表示是白色,wi = 1 表示是黑色

输出格式

输出到文件 wait.out 中。
如果无解,输出 −1。
否则输出一行 m 个 0/1 数表示每个区间是黑色还是白色。如果有多解,输出任意一个均可得分。

做法

离散化后,我们就只需要考虑区间的端点处的个数差。
把每个数轴上的点对应到一个图上的点,一个区间\([l_i,r_i]\)视作\(l_i\)\(r_i\)间的一条有向边,其中黑色为从\(l_i\)\(r_i\),白色相反,染色视作定向。
如果沿着每条边遍历各个节点,那么这就是一个欧拉路径问题。所以把奇度数的点排序后依次连接,跑欧拉回路即可。
一部分线段染色相当于是混合图欧拉回路,跑网络流即可。

代码

#include <bits/stdc++.h>
#define maxn 200100
using namespace std;
namespace WXHAK
{
struct edge
{
    int r, nxt, w, id;
} e[maxn << 2];
int head[maxn], S, T, q[maxn], esz, l, r, cur[maxn], dep[maxn];
void addedge(int u, int v, int w, int id)
{
    e[++esz].r = v;
    e[esz].nxt = head[u];
    head[u] = esz;
    e[esz].w = w;
    e[esz].id = id;
    e[++esz].r = u;
    e[esz].nxt = head[v];
    head[v] = esz;
    e[esz].w = 0;
    e[esz].id = -id;
}
bool bfs()
{
    for (int i = S; i <= T; ++i)
        cur[i] = head[i], dep[i] = 0;
    l = r = 0, q[r++] = S, dep[S] = 1;
    while (l < r)
    {
        int u = q[l++];
        for (int t = head[u]; t; t = e[t].nxt)
            if (e[t].w && !dep[e[t].r])
                dep[e[t].r] = dep[u] + 1, q[r++] = e[t].r;
    }
    return dep[T] != 0;
}
int find(int u, int flow)
{
    if (u == T)
        return flow;
    int used = 0, a = 0;
    for (int &t = cur[u]; t; t = e[t].nxt)
        if (e[t].w && dep[e[t].r] == dep[u] + 1)
        {
            a = find(e[t].r, min(flow - used, e[t].w));
            e[t].w -= a, e[t ^ 1].w += a, used += a;
            if (used == flow)
                return used;
        }
    if (!used)
        dep[u] = -1;
    return used;
}
void init(int _S, int _T)
{
    S = _S, T = _T, esz = 1;
    for (int i = S; i <= T; ++i)
        head[i] = 0;
}
int dinic()
{
    int ans = 0, a = 0;
    while (bfs())
        while (a = find(S, 1 << 30))
            ans += a;
    return ans;
}
void cal(int anses[])
{
    for (int i = 1; i <= esz; ++i)
        if (!e[i].w && e[i].id)
            anses[abs(e[i].id)] = e[i].id > 0 ? !anses[abs(e[i].id)] : anses[abs(e[i].id)];
}
} // namespace WXHAK

int mtp, in[maxn], out[maxn], anses[maxn], tg[maxn];
int li[maxn], ri[maxn], lx[maxn], wi[maxn], lsh[maxn << 1], tp, n, m;
int main()
{
    freopen("wait.in", "r", stdin);
    freopen("wait.out", "w", stdout);
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; ++i)
    {
        scanf("%d%d%d", &li[i], &ri[i], &wi[i]);
        ri[i]++;
        lsh[++tp] = li[i], lsh[++tp] = ri[i];
        lx[++mtp] = li[i], lx[++mtp] = ri[i];
    }]
    sort(lsh + 1, lsh + tp + 1);
    sort(lx + 1, lx + mtp + 1);
    tp = unique(lsh + 1, lsh + tp + 1) - lsh - 1;
    for (int i = 1; i <= m; ++i)
    {
        li[i] = lower_bound(lsh + 1, lsh + tp + 1, li[i]) - lsh;
        ri[i] = lower_bound(lsh + 1, lsh + tp + 1, ri[i]) - lsh;
    }]
    for (int i = 1; i <= mtp; i += 2)
    {
        int x = lower_bound(lsh + 1, lsh + tp + 1, lx[i]) - lsh;
        int y = lower_bound(lsh + 1, lsh + tp + 1, lx[i + 1]) - lsh;
        if (x == y)
            continue;
        tg[i] = rand() % 2;
        if (tg[i] == 0)
            in[y]++, out[x]++;
        else
            in[x]++, out[y]++;
    }
    for (int i = 1; i <= m; ++i)
    {
        int r = (wi[i] == -1 ? rand() % 2 : wi[i]);
        anses[i] = r;
        if (r == 0)
            out[li[i]]++, in[ri[i]]++;
        else
            out[ri[i]]++, in[li[i]]++;
    }
    WXHAK::init(0, tp + 1);
    int sum = 0;
    for (int i = 1; i <= tp; ++i)
        if ((in[i] - out[i]) & 1)
            return puts("-1"), 0;
    for (int i = 1; i <= tp; ++i)
    {
        int d = (in[i] - out[i]) / 2;
        if (d > 0)
            WXHAK::addedge(WXHAK::S, i, d, 0), sum += d;
        else if (d < 0)
            WXHAK::addedge(i, WXHAK::T, -d, 0);
    }
    for (int i = 1; i <= m; ++i)
        if (wi[i] == -1)
        {
            if (anses[i] == 0)
                WXHAK::addedge(ri[i], li[i], 1, i);
            else
                WXHAK::addedge(li[i], ri[i], 1, i);
        }
    for (int i = 1; i <= mtp; i += 2)
    {
        int x = lower_bound(lsh + 1, lsh + tp + 1, lx[i]) - lsh;
        int y = lower_bound(lsh + 1, lsh + tp + 1, lx[i + 1]) - lsh;
        if (x == y)
            continue;
        if (tg[i] == 0)
            WXHAK::addedge(y, x, 1, 0);
        else
            WXHAK::addedge(x, y, 1, 0);
    }
    int wxh = WXHAK::dinic();
    if (wxh != sum)
        return puts("-1"), 0;
    WXHAK::cal(anses);
    for (int i = 1; i <= m; ++i)
    {
        if (wi[i] != -1)
            printf("%d ", wi[i]);
        else
            printf("%d ", anses[i]);
    }
}

简单分析一下:
输入结束后,首先在91~98行离散化。
99~110行,对相邻的两个点之间的边定一个任意的方向。
111~119行,对每个区间对应的边确定方向(有限定的不变)。
125~132行,源点、汇点加入网络。
133~140行,141~151行,这两种边分别加入网络。
4~75及152~164行,网络流。

转载于:https://www.cnblogs.com/water-lift/p/10946785.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值