我图呢
题目链接:jzoj 6683
题目大意
给你一些点,再给出几组关系:某两个点之间不会都被选到。
这些关系满足一个条件:你可以选出一些点,使得上面的关系都刚好有一个其中一个点在你选到的点中。
要你给出一种选的方案,使得选的点最多,在选的最多的基础上还要求选的点的点权和最大。(每个点会给出点权)
还要你输出方案。
思路
这道题用网络流来做。
然后就是神奇的建图,之前也是做网络流似乎用过这个思路。
就是你是选的点个数优先最大,点权次要。
那我们就让点个数变成一个很大的数,这样再跑网络流,就自然会优先搞点个数。然后到时用除和取模,就可以得到两个的答案
然后就是建图,首先它的条件你推一推你就会发现他的意思就是不能存在关系的奇环。
那就代表可以黑白染色,然后就可以二分图。
那就有了,点连向源点或汇点,流量是点的个数(记得要变成很大值)加上选这个点的点权,然后点之间连边,流量是一个更大的值。
然后不难想到跑的就是最小割。
那接着求最小割的方案就是 bfs,从源点跑还有残余网络的边。把点分成能跑到和不能跑到的。
那之后就分成了两种,在左边,不能流到右边的就说明这个被断了,就说明要选它。
然后在右边,能留到左边的,就说明这个被断了,就说明要选它。
然后就有了方案了。
(点权和你取模一下就有了)
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define di 10000000000
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct line {
int to, nxt;
}e_[2000001];
struct node {
ll x;
int to, nxt, op;
}e[5000001];
int n, m, x, y, le[1001], KK, a[301], S, T;
int le_[301], KK_, lee[1001], dis[1001];
bool black[301], in[301];
queue <int> q;
ll sum, ans;
void add_line(int x, int y) {
e_[++KK_] = (line){y, le_[x]}; le_[x] = KK_;
e_[++KK_] = (line){x, le_[y]}; le_[y] = KK_;
}
void dfs(int now, int father, int op) {//跑图,黑白染色
black[now] = op;
in[now] = 1;
for (int i = le_[now]; i; i = e_[i].nxt)
if (e_[i].to != father && !in[e_[i].to]) {
dfs(e_[i].to, now, op ^ 1);
}
}
void add(int x, int y, ll z) {
e[++KK] = (node){z, y, le[x], KK + 1}; le[x] = KK;
e[++KK] = (node){0, x, le[y], KK - 1}; le[y] = KK;
}
//网络流
bool bfs() {
for (int i = 1; i <= T; i++)
lee[i] = le[i];
while (!q.empty()) q.pop();
memset(dis, 0, sizeof(dis));
dis[S] = 1;
q.push(S);
while (!q.empty()) {
int now = q.front();
q.pop();
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x && !dis[e[i].to]) {
dis[e[i].to] = dis[now] + 1;
if (e[i].to == T) return 1;
q.push(e[i].to);
}
}
return 0;
}
ll dfs1(int now, ll sum) {
if (now == T) return sum;
ll go = 0;
for (int &i = lee[now]; i; i = e[i].nxt)
if (e[i].x && dis[e[i].to] == dis[now] + 1) {
ll this_go = dfs1(e[i].to, min(sum - go, e[i].x));
if (this_go) {
e[i].x -= this_go;
e[e[i].op].x += this_go;
go += this_go;
if (go == sum) return go;
}
}
return go;
}
//得出割的方案
void get_fang(int now) {
in[now] = 1;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].x > 0 && !in[e[i].to])
get_fang(e[i].to);
}
void dinic() {
while(bfs())
ans += dfs1(S, INF);
ans = sum + n * di - ans;
memset(in, 0, sizeof(in));
get_fang(S);
int num = 0;
for (int i = 1; i <= n; i++)
if (in[i] ^ black[i] == 0) num++;
printf("%d %d\n", num, ans % di);
for (int i = 1; i <= n; i++)
printf("%d", in[i] ^ black[i] == 0);
}
int main() {
// freopen("graph.in", "r", stdin);
// freopen("graph.out", "w", stdout);
scanf("%d %d", &n, &m);
S = n + 1;
T = n + 2;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
sum += a[i];
}
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
add_line(x, y);
}
for (int i = 1; i <= n; i++)
if (!in[i]) dfs(i, 0, 0);
//建图
for (int i = 1; i <= n; i++) {
if (black[i]) {
add(S, i, 1ll * a[i] + di);
for (int j = le_[i]; j; j = e_[j].nxt)
add(i, e_[j].to, INF);
}
else {
add(i, T, 1ll * a[i] + di);
}
}
dinic();
fclose(stdin);
fclose(stdout);
return 0;
}