【NOI2015】小园丁与老司机 DP 网络流

14 篇文章 0 订阅
1 篇文章 0 订阅

一开始你在原点,有n棵许愿树,你每次可以向左,右,左上,右上,上到达最近的一棵许愿树许愿。问最多能到达多少许愿树,输出方案。在所有可能的路径中,保留所有非左右的边,问最小路径覆盖所有的边。n <= 50000, y坐标相同的点不超过1000

首先按y坐标排序,使用map进行转移,同层的点一起转移。

建出第三问的图后,考虑一个点的入度和出度,如果路径不可相交,答案就是Σmax(in[x] - out[x], 0),考虑增建平行边,就可以网络流了。

/**************************************************************
    Problem: 4200
    User: hzt1
    Language: C++
    Result: Accepted
    Time:2696 ms
    Memory:222476 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 51000, inf = 1 << 29;
struct node { int x, y, n; } a[N];
struct Edge { int y, nex, z; } g[N*20];
int n, sz = 1, pos[N], f[N], d[N], out[N], S, T, ans, pr[N][5], lx[N], ly[N], py[N][1002], que[N], dis[N], so, si, in[N];
int sum, hd, tl; bool va[N][2], nof[N];
map<int, int> m1, m2, mx;
void Init(int x, int y, int z) { g[++ sz] = (Edge) { y, pos[x], z }, pos[x] = sz; }
void Add(int x, int y, int z) { Init(x, y, z), Init(y, x, 0); }
bool cmp(node x, node y) { return (x.y == y.y) ? x.x < y.x : x.y < y.y; }
void Renew(int x, int &z) {
    if (z) {
        if (f[x] <= d[z]) f[x] = d[z] + 1, pr[x][ lx[x] = 1 ] = z;
        else if (f[x] == d[z] + 1) pr[x][++ lx[x]] = z;
    }
    z = x;
}
void Print(int x, bool ty) {
    if (!x) return ;
    if (ty) {
        int y = py[x][1]; Print(y, 0);
        if (x == y) return ;
        if (y < x) {
            for (int z = y - 1; a[z].y == a[y].y; z --) printf("%d ", a[z].n);
            Rep(z, y + 1, x) printf("%d ", a[z].n);
        } else {
            for (int z = y + 1; a[z].y == a[y].y; z ++) printf("%d ", a[z].n);
            Dwn(z, y - 1, x) printf("%d ", a[z].n);
        }
    } else Print(pr[x][1], 1), printf("%d ", a[x].n);
}
void Build(int x, bool ty) {
    if (!x || va[x][ty]) return ;
    va[x][ty] = 1;
    if (ty) Rep(i, 1, ly[x]) Build(py[x][i], 0);
    else Rep(i, 1, lx[x]) { int y = pr[x][i]; out[y] ++, in[x] ++, Build(y, 1), Add(y, x, inf); }
}
bool Bfs() {
    memset(dis, 0, sizeof(dis)), dis[S] = 1;
    que[hd = tl = 1] = S;
    while (hd <= tl) {
        int x = que[hd ++];
        RepE(i, x) {
            int y = g[i].y;
            if (!dis[y] && g[i].z) dis[y] = dis[x] + 1, que[++ tl] = y;
        }
    }
    return dis[T];
}
int Dfs(int x, int mx) {
    if (x == T) return mx;
    if (nof[x]) return 0;
    int ret = 0;
    RepE(i, x) {
        int y = g[i].y, k;
        if (mx && dis[y] == dis[x] + 1 && g[i].z && (k = Dfs(y, min(mx, g[i].z)))) {
            ret += k, g[i].z -= k, g[i ^ 1].z += k, mx -= k;
        }
    }
    if (!ret) nof[x] = 1;
    return ret;
}
void Dinic() {
    int ret = 0;
    while (Bfs()) {
        memset(nof, 0, sizeof(nof));
        while (ret = Dfs(S, inf)) sum -= ret;
    }
}
int main()
{
    scanf ("%d", &n), S = n + 1, T = n + 2;
    Rep(i, 1, n) scanf ("%d%d", &a[i].x, &a[i].y), a[i].n = i;
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1, lt = 0; i <= n; i = lt + 1) {
        while (lt < n && a[lt + 1].y == a[i].y) {
            lt ++; int x = a[lt].x, y = a[lt].y;
            if (x == y || x == -y || !x) f[lt] = 1, pr[lt][ lx[lt] = 1 ] = 0; else f[lt] = -n;
            Renew(lt, mx[x]), Renew(lt, m1[x + y]), Renew(lt, m2[x - y]);
        }
        int w;
        Rep(j, i, lt) {
            w = f[j]; py[j][ ly[j] = 1 ] = j;
            Rep(k, i, j - 1)
                if (f[k] + j - i > w) w = f[k] + j - i, py[j][ ly[j] = 1 ] = k;
                else if (f[k] + j - i == w) py[j][ ++ ly[j] ] = k;
            d[j] = w;
        }
        Dwn(j, lt, i) {
            w = d[j];
            Rep(k, j + 1, lt)
                if (f[k] + lt - j > w) w = f[k] + lt - j, py[j][ ly[j] = 1 ] = k;
                else if (f[k] + lt - j == w) py[j][ ++ ly[j] ] = k;
            d[j] = w; ans = max(ans, w);
        }
    }
    printf("%d\n", ans);
    Rep(i, 1, n) if (d[i] == ans) { Print(i, 1); puts(""); break ; }
    Rep(i, 1, n) if (d[i] == ans) Build(i, 1);
    Rep(i, 0, n) {
        if (in[i] > out[i]) Add(S, i, in[i] - out[i]), sum += in[i] - out[i];
        else Add(i, T, out[i] - in[i]);
    }
    Dinic();
    printf("%d\n", sum);
 
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值