吐槽
这题目真的很吸引人
样例不能复制真是可惜
题目的方格染色以及奇怪的节点让我想起了以前看过的一篇博文,讲最小割的。。
于是这道题就最小割。。。
题解
好吧假设我们不知道这道题要最小割
看着题目给的式子我们需要对它做♂点事情。
我们令 xi=1 表示点i为黑, xi=0 表示点i为白。那么上面的式子可以写成:
先在这个式子就与线性规划转化为最小割的式子长的比较像了,但是转化的时候要求最小化且函数内部不能有减号,所以我们还要修改这个式子。
这个式子符合我们的要求,按照这个式子构图就好了。
需要注意的是这个式子有约束条件,发现对于每个 xi , ∑max{xi−xj,0} 的值不能超过1,因为如果i奇怪 pi 的值只用计一次。
因此我们这么构图。
源点连向 i ,边权为
图构完了,但是满足条件的
(i,j)
可能是
O(n2)
的,所以我们考虑请出权值线段树。
如果
j
在
但是没有考虑
j<i
,所以我们需要可持久化线段树限制区间。
没有图,可以看PoPoQQQ神的文。
那么构建可持久化线段树时,我们要插入
ai
,所以在插入过程中改图,每新建一条树边自然要在图中对应加边,还有
i
也要指向包含
然后查询可持久化线段树的
[li,ri]
区间,并将正好整个在这个区间内的点全部连向
i′
。
于是问题解决了。
Coding Summary
为什么别人的程序都这么快。。
难道姿势不一样QAQ
真的是痛苦。。
各种脑抽写错,程序重写了2遍。。
原来SegTree的缺省构造函数里面包括了自增id,然而operator new里面定义的大数组也会自增id。
然后不说占用了大量无用id,各种爆数组。。之前还奇怪为什么会RE。。
有时候不能手贱,缺省构造函数还是要谨慎一些。
样例文本:
10
0 1 7 3 9 2
7 4 0 9 10 5
1 0 4 2 10 2
7 9 1 5 7 2
6 3 5 3 6 2
6 6 4 1 8 1
6 1 6 0 6 5
2 2 5 0 9 3
5 1 3 0 2 5
5 6 7 1 1 2
Summary
- 网络中若存在大量的无限容量的边,可以调整这些边使边数更少。
- 由题意得到的数学表达式若存在2种对立状态和其他与这2种状态有关的状态,可以采用最小割,将数学表达式改写为最小割线性规划表达形式。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 8192, M = 262144, inf = 1000000000, EDGE = 1048576;
struct SegTree {
static int timer;
static void put_callback(SegTree *node, SegTree *new_node, int f);
static void get_callback(SegTree *node, int x);
SegTree *lc, *rc;
int v, s;
SegTree() { lc = rc = NULL; v = s = 0; }
SegTree(SegTree *a, SegTree *b, int c) : lc(a), rc(b), v(c), s(++timer) {}
void *operator new(size_t) {
static SegTree pool[M], *C = pool;
return C++;
}
SegTree *put(int l, int r, int t, int f) {
int mid = l + r >> 1;
SegTree *rt;
if (l == r) rt = new SegTree(NULL, NULL, v + 1);
else if (t <= mid) rt = new SegTree(lc->put(l, mid, t, f), rc, v + 1);
else rt = new SegTree(lc, rc->put(mid + 1, r, t, f), v + 1);
put_callback(this, rt, f);
return rt;
}
void get(int l, int r, int ql, int qr, int x) {
int mid = l + r >> 1;
if (!v) return;
if (ql == l && r == qr) {
get_callback(this, x);
return;
}
if (ql > mid) rc->get(mid + 1, r, ql, qr, x);
else if (qr <= mid) lc->get(l, mid, ql, qr, x);
else lc->get(l, mid, ql, mid, x), rc->get(mid + 1, r, mid + 1, qr, x);
}
} *tree[N];
int SegTree::timer = 0;
struct MaxFlow {
int s, t, cnt;
int head[M], next[EDGE], w[EDGE], to[EDGE], level[M];
MaxFlow(int _s, int _t) : cnt(0), s(_s), t(_t) {
memset(head, -1, sizeof head);
}
void add(int u, int v, int c) {
next[cnt] = head[u]; to[cnt] = v; w[cnt] = c; head[u] = cnt++;
next[cnt] = head[v]; to[cnt] = u; w[cnt] = 0; head[v] = cnt++;
}
bool bfs() {
static int q[M];
memset(level, -1, sizeof level);
int f = 0, r = 0;
q[r++] = s; level[s] = 0;
while (f < r) {
int u = q[f++];
for (int i = head[u]; i != -1; i = next[i])
if (w[i] > 0 && level[to[i]] == -1) {
level[to[i]] = level[u] + 1;
q[r++] = to[i];
}
}
return level[t] != -1;
}
int dfs(int k, int low) {
if (k == t) return low;
int res = 0, tmp, i;
for (i = head[k]; i != -1 && res < low; i = next[i])
if (level[to[i]] == level[k] + 1 && w[i] > 0) {
tmp = dfs(to[i], min(low - res, w[i]));
w[i] -= tmp; w[i^1] += tmp; res += tmp;
}
if (!res) level[k] = -1;
return res;
}
ll solve() {
ll ans = 0;
while (bfs()) ans += dfs(s, inf);
return ans;
}
} *maxflow;
void SegTree::put_callback(SegTree *t, SegTree *nt, int f) {
maxflow->add(f, nt->s, inf);
maxflow->add(t->s, nt->s, inf);
}
void SegTree::get_callback(SegTree *t, int x) {
maxflow->add(t->s, x, inf);
}
int main() {
#define p2(i) ((i)*2)
#define p1(i) ((i)*2-1)
int i, a, b, w, l, r, p, n;
ll ans = 0;
scanf("%d", &n);
maxflow = new MaxFlow(0, M - 1);
SegTree::timer = p2(n);
tree[0] = new SegTree(NULL, NULL, 0);
tree[0]->lc = tree[0]->rc = tree[0];
for (i = 1; i <= n; i++) {
scanf("%d%d%d%d%d%d", &a, &b, &w, &l, &r, &p);
maxflow->add(maxflow->s, p1(i), w);
maxflow->add(p1(i), maxflow->t, b);
tree[i] = tree[i - 1]->put(0, inf, a, p1(i));
tree[i - 1]->get(0, inf, l, r, p2(i));
maxflow->add(p2(i), p1(i), p);
ans += w + b;
}
printf("%lld", ans - maxflow->solve());
return 0;
}
数据生成器
写了个最低难度的数据生成器。。真是不写没法调试。。
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;
int random(int sz) {
return ((double)rand())/RAND_MAX*sz+1;
}
int main() {
freopen("input.in", "w", stdout);
int n = 5000, maxA = 1e9, maxV = 2e5, maxP = 3e5;
srand(time(NULL));
printf("%d\n", n);
for (int i = 1; i <= n; i++) {
int c = random(maxA), x = random(min(c, maxA - c));
printf("%d %d %d %d %d %d\n", random(maxA), random(maxV), random(maxV), c - x, c + x, random(maxP));
}
return 0;
}
题目
相关博客:http://blog.csdn.net/regina8023/article/details/42873403
http://www.cnblogs.com/snake-hand/p/3144807.html
http://blog.csdn.net/PoPoQQQ/article/details/42557217