首页 > 题解 > bzoj 4625 [BeiJing2016]水晶

bzoj 4625 [BeiJing2016]水晶

Description

不用惊慌,今天的题都不是小强出的。——融入了无数心血的作品,现在却不得不亲手毁掉,难以体会他的心情啊

。——那也是没有办法的事情,能量共振不消除的话……望着已经被装上炸药的水晶,02放下了望远镜,看向了手中的共振分析报告。还是会有一些水晶,幸存下来的……也许吧。地图由密铺的六边形单元组成,每个单元与其他六个单元相邻。为了方便起见,我们用坐标(x,y,z)描述一个单元的位置,表示从原点开始按如图所示的x,y,z方向各走若干步之后到达的地方。有可能有两个坐标描述同一个单元,比如(1,1,1)和(0,0,0)描述的都是原点

显然(x,y,z)单元和(x+1, y,z),(x-1,y,z),(x,y+1,z),(x,y-1,z),(x, y, z+1),(x,y, z-1)相邻。有N块水晶位于地图的单元内,第i块水晶位于坐标(xi, yi, zi)所表示的单元中,并拥有ci的价值。每个单元内部可能会有多块水晶。地图中,有一些单元安装有能量源。如下图,任何满足x+y+z是3的整数倍的坐标所描述的单元内都安装有能量源。

有能量源的单元中的水晶价值将会额外增加10%.如果三块水晶所在的单元满足特定排列,那么它们将会引发共振。共振分两种,a共振和b共振。a共振:如果三块水晶所在的单元两两相邻地排成一个三角形,那么会引起a共振。

图中每一个三角形表示这三个单元各有一块水晶将会发生一个a共振。b共振:如果三块水晶所在的单元依次相邻地排成一条长度为2的直线段,且正中间的单元恰好有能量源,那么会引起b共振。

图中粉红色线段表示这三个单元各有一块水晶将会发生一个b共振,黑色线段表示即使这三个单元有水晶也不会发生b共振。现在你要炸掉一部分水晶,使得任何共振都不会发生的前提下,剩余水晶的价值总和最大。

Input

第一行是一个正整数N,表示水晶数量。
接下来N行,每行四个整数xi,yi,zi,ci,空格分隔,表示一个水晶的位
置和价值。有可能有水晶的位置重合。
N≤50000,1≤ci≤1000,|xi|, |yi|, |zi|≤1000.

Output

仅一行,一个实数,表示剩余水晶的价值总和。四舍五入保留1位小数。

Sample Input

4
0 0 0 11
1 0 0 5
0 1 0 7
0 0 -1 13

Sample Output

25.1

【样例说明1】

四块水晶排成一个菱形,没有b共振,有2处a共振,分别是1, 2, 4号水晶

和1, 3, 4号水晶形成的三角形。

因此,为了消除两处a共振,有如下3种方案:

因此,为了消除两处a共振,有如下3种方案

  1. 炸掉1号水晶,留下2, 3, 4号水晶,总剩余价值5+7+13=25.
  2. 炸掉4号水晶,留下1, 2, 3号水晶,总剩余价值11×(1+10%)+5+7=24.1.

  3. 炸掉2, 3号水晶,留下1, 4号水晶,总剩余价值11×(1+10%)+13=25.1.

因此我们采用第三种方案,最大总剩余价值为25.1.

题解

首先把显然可以把这图转换成一个二维的图。。

考虑每个在能量源的水晶,把无能量源的点黑白染色,则要不炸掉能量源,要不炸掉全黑,要不炸掉全白,离散变量有三个取值S=val>白=inf>A=1.1val>B=inf>黑=val>T。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#define N 100010
using namespace std;
const int inf = 0x3f3f3f3f;
struct flows {
    struct node {
        int to, next, flow;
    } e[N * 20];
    int tot, st[N], dis[N], cur[N];
    void init() {
        tot = -1, memset(e, -1, sizeof e),
        memset(st, -1, sizeof st);
    }
    void add(int x, int y, int z) {
        e[++tot].to = y;
        e[tot].flow = z;
        e[tot].next = st[x];
        st[x] = tot;
    }
    void add_edge(int x, int y, int z) {
//      printf("add%d %d %d\n", x, y, z);
        add(x, y, z), add(y, x, 0);
    }
    queue <int> que;
    int bfs(int S, int T) {
        memcpy(cur, st, sizeof st);
        memset(dis, 0, sizeof dis);
        while (!que.empty()) que.pop();
        que.push(S);
        dis[S] = 1;
        while(!que.empty()) {
            int now = que.front();
            que.pop();
            for (int i = st[now]; ~i; i = e[i].next)
                if (e[i].flow && !dis[e[i].to]) {
                    dis[e[i].to] = dis[now] + 1;
                    if (e[i].to == T) return 1;
                    que.push(e[i].to);
                }
        }
        return 0;
    }
    int finds(int now, int T, int flow) {
        if (now == T)
            return flow;
        int f;
        for (int i = cur[now]; ~i; i = e[i].next) {
            cur[now] = i;
            if (e[i].flow && dis[e[i].to] == dis[now] + 1 &&
                (f = finds(e[i].to, T, min(flow, e[i].flow)))) {
                e[i].flow -= f;
                e[i^1].flow += f;
                return f;
            }
        }
        return 0;
    }
    int dinic(int S, int T) {
        int flow = 0, x;
        while(bfs(S, T)) {
            while(x = finds(S, T, inf)) {
                flow += x;
            }
        }
        return flow;
    }
}flow;
int ge(int x, int y) { return (x + 2000) * 4000 + y + 2000; }
bool mark[N];
map <int, int> ma;
struct data {
    int x, y, z, c;
}a[N];
int n, ans;
bool ins(data p, int id) {
    int now = ge(p.x, p.y);
    if (ma[now])
        return a[ma[now]].c += p.c, 1;
    else
        return 0 * (ma[now] = id);
}
main() {
    scanf("%d", &n);
    int S = 0, T = 100001;
    flow.init();
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d%d", &a[i].x, &a[i].y, &a[i].z, &a[i].c), a[i].c *= 10;
        a[i].x -= a[i].z, a[i].y -= a[i].z, a[i].z = 0;
        if ((a[i].x + a[i].y) % 3 == 0) a[i].c += a[i].c / 10;
        if (ins(a[i], i)) mark[i] = 1;
        ans += a[i].c;
    }
    for (int i = 1; i <= n; ++i)
        if (!mark[i]) {
            flow.add_edge(i, i + n, a[i].c);
            if ((a[i].x + a[i].y + 30000) % 3 == 2)
                flow.add_edge(S, i, inf);
            if ((a[i].x + a[i].y + 30000) % 3 != 1) {
                flow.add_edge(i + n, ma[ge(a[i].x, a[i].y + 1)], inf);
                flow.add_edge(i + n, ma[ge(a[i].x + 1, a[i].y)], inf);
                flow.add_edge(i + n, ma[ge(a[i].x - 1, a[i].y - 1)], inf);
            } else {
                flow.add_edge(i + n, T, inf);
            }
        }
    printf("%.1lf", (double) (ans - flow.dinic(S, T)) / 10.0);
}