[BZOJ4625][BeiJing2016]水晶

[BZOJ4625][BeiJing2016]水晶

试题描述

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

。——那也是没有办法的事情,能量共振不消除的话……望着已经被装上**的水晶,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共振。现在你要炸掉一部分水晶,使得任何共振都不会发生的前提下,剩余水晶的价值总和最大。

输入

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

输出

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

输入示例

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

输出示例

25.1

数据规模及约定

见“输入

题解

我们发现两种共振的情况都包含且仅包含了一个能量源,那么我们把能量源的六边形先盖住不看,剩下的六边形的相邻关系构成了一个二分图,我们对这个二分图进行黑白染色,发现这两种共振都包含了一黑一白。

于是得出结论:对于相邻的黑、白和能量源我们要么不选能量源,黑白任意;要么选能量源和同色的。

于是建出最小割模型:源点向白点连容量为权值的边;每个能量源拆成两个点 A 和 B,相邻的白点向 A,B 向相邻的黑点连无穷的边,A 向 B 连权值 × 1.1 的边;黑点向汇点连容量为权值的边。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define maxm 600010
#define maxx 4010
#define oo 2147483647
#define pii pair <int, int>

struct Edge {
	int from, to, flow;
	Edge() {}
	Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {}
};
struct Dinic {
	int n, m, s, t, head[maxn], nxt[maxm];
	Edge es[maxm];
	int Q[maxn], hd, tl, vis[maxn];
	int cur[maxn];
	void init() {
		m = 0; memset(head, -1, sizeof(head));
		return ;
	}
	void setn(int _) { n = _; return ; }
	void AddEdge(int a, int b, int c) {
		es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++;
		es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++;
		return ;
	}
	bool BFS() {
		memset(vis, 0, sizeof(vis));
		hd = tl = 0; Q[++tl] = s;
		vis[s] = 1;
		while(hd < tl) {
			int u = Q[++hd];
			for(int i = head[u]; i != -1; i = nxt[i]) {
				Edge& e = es[i];
				if(e.flow && !vis[e.to]) {
					vis[e.to] = vis[u] + 1;
					Q[++tl] = e.to;
				}
			}
		}
		return vis[t] > 0;
	}
	int DFS(int u, int a) {
		if(u == t || !a) return a;
		int f, flow = 0;
		for(int& i = cur[u]; i != -1; i = nxt[i]) {
			Edge& e = es[i];
			if(vis[e.to] == vis[u] + 1 && (f = DFS(e.to, min(a, e.flow)))) {
				flow += f; a -= f;
				e.flow -= f; es[i^1].flow += f;
				if(!a) return flow;
			}
		}
		return flow;
	}
	int MaxFlow(int _s, int _t) {
		s = _s; t = _t;
		int flow = 0;
		while(BFS()) {
			for(int i = 1; i <= n; i++) cur[i] = head[i];
			flow += DFS(s, oo);
		}
		return flow;
	}
} sol;

int Cnt;
struct Node {
	int id, x, y;
	int val;
	Node(): id(0) {}
	Node(int _1, int _2, int _3): id(0), x(_1), y(_2), val(_3) {}
	int p() { return id ? id : id = ++Cnt; }
} ns[maxn], n2[maxn], Source, Tank;

vector <int> white, black, middle;
int Map[maxx][maxx], Mid[maxx][maxx];

void add(vector <int>& A, int x, int y) {
	if(0 <= x && x < maxx && 0 <= y && y < maxx) ;
	else return ;
	if(Mid[x][y]) A.push_back(Mid[x][y]);
	return ;
}
void add_white(int x, int y) {
	if(0 <= x && x < maxx && 0 <= y && y < maxx) ;
	else return ;
	add(white, x - 1, y);
	add(white, x, y - 1);
	add(white, x + 1, y + 1);
	int u = Mid[x][y];
	if(Mid[x-1][y]) sol.AddEdge(ns[Mid[x-1][y]].p(), ns[u].p(), oo);
	if(Mid[x][y-1]) sol.AddEdge(ns[Mid[x][y-1]].p(), ns[u].p(), oo);
	if(Mid[x+1][y+1]) sol.AddEdge(ns[Mid[x+1][y+1]].p(), ns[u].p(), oo);
	return ;
}
void add_black(int x, int y) {
	if(0 <= x && x < maxx && 0 <= y && y < maxx) ;
	else return ;
	add(black, x + 1, y);
	add(black, x, y + 1);
	add(black, x - 1, y - 1);
	int u = Mid[x][y];
	if(Mid[x+1][y]) sol.AddEdge(n2[u].p(), ns[Mid[x+1][y]].p(), oo);
	if(Mid[x][y+1]) sol.AddEdge(n2[u].p(), ns[Mid[x][y+1]].p(), oo);
	if(Mid[x-1][y-1]) sol.AddEdge(n2[u].p(), ns[Mid[x-1][y-1]].p(), oo);
	return ;
}

#define vit vector <int> :: iterator

int main() {
	int n = read(); sol.init();
	int sum = 0;
	for(int i = 1; i <= n; i++) {
		int x = read(), y = read(), z = read(), v = read();
		x -= z; y -= z;
		x += 2000; y += 2000;
		Map[x][y] += v;
	}
	for(int x = 0, i = 0; x < maxx; x++)
		for(int y = 0; y < maxx; y++) if(Map[x][y]) {
			int v = Map[x][y]; Mid[x][y] = ++i;
			ns[i] = Node(x, y, (x + y - 4000) % 3 ? v * 10 : v * 11);
			sum += ns[i].val;
			if((x + y - 4000) % 3 == 0) middle.push_back(i);
		}
	for(vit i = middle.begin(); i != middle.end(); i++) {
		int x = ns[*i].x, y = ns[*i].y;
		sol.AddEdge(ns[*i].p(), n2[*i].p(), ns[*i].val);
		add_white(x, y);
		add_black(x, y);
	}
	sort(white.begin(), white.end());
	vit it = unique(white.begin(), white.end());
	for(vit i = white.begin(); i != it; i++) sol.AddEdge(Source.p(), ns[*i].p(), ns[*i].val);
	sort(black.begin(), black.end());
	it = unique(black.begin(), black.end());
	for(vit i = black.begin(); i != it; i++) sol.AddEdge(ns[*i].p(), Tank.p(), ns[*i].val);
	sol.setn(Cnt);
	
	printf("%.1lf
", (sum - sol.MaxFlow(Source.p(), Tank.p())) / 10.0);
	
	return 0;
}

Dinic 里面 BFS() 中如果忘了 vis[s] = 1 这句话会 T。。。

原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6703567.html