SDOI2014 Lis(网络流)

题目链接

题目大意

题目已经说得很清楚了qwq。

题解

首先我们考虑如何计算出最少删除的代价。
考虑在dp的过程中,如果 i i i能够使 j j j取到最大值,就连边 ( i , j ) (i,j) (i,j),容易发现,题目就是问去掉一些点使左右不连通求最小代价。
于是拆点连边跑最小割即可。
但是题目还要字典序最小,我们考虑按C从小到大排序,判断当前边是否在割中,如果在,就割掉。然鹅我们必须及时更新每条边的剩余容量,因此每次割掉之后还要跑最大流。但是这样显然会T,于是就需要退流。
( u , v ) (u,v) (u,v)删掉后退流的过程就是跑 u → S u\rightarrow S uS的最大流,再跑 T → v T\rightarrow v Tv的最大流即可。
然后就过了。

#include <bits/stdc++.h>
using namespace std;
const int MAXR = 10000000;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
	return getchar();
#endif
	if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
	char c = _READ_[_READ_POS_++];
	if (_READ_POS_ == MAXR) _READ_POS_ = 0;
	if (_READ_POS_ > _READ_LEN_) return 0;
	return c;
}
template<typename T> inline void read(T &x) {
	x = 0; register int flag = 1, c;
	while (((c = readc()) < '0' || c > '9') && c != '-');
	if (c == '-') flag = -1; else x = c - '0';
	while ((c = readc()) >= '0' && c <= '9') x = x * 10 + c - '0';
	x *= flag;
}
template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
	read(a), read(x...);
}
inline int reads(char *s) {
	register int len = 0, c;
	while (isspace(c = readc()) || !c);
	s[len++] = c;
	while (!isspace(c = readc()) && c) s[len++] = c;
	s[len] = 0;
	return len;
}
inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
inline void printc(char c) {
	_PRINT_[_PRINT_POS_++] = c;
	if (_PRINT_POS_ == MAXR) ioflush();
}
inline void prints(char *s) {
	for (int i = 0; s[i]; i++) printc(s[i]);
}
template<typename T> inline void print(T x, char c = '\n') {
	if (x < 0) printc('-'), x = -x;
	if (x) {
		static char sta[20];
		register int tp = 0;
		for (; x; x /= 10) sta[tp++] = x % 10 + '0';
		while (tp > 0) printc(sta[--tp]);
	} else printc('0');
	printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
	print(x, ' '), print(y...);
}
typedef long long ll;

const int MAXN = 2005, MAXM = 1000005, INF = 0x3f3f3f3f;
struct Edge { int to, cap, next; } edge[MAXM];
int head[MAXN], que[MAXN], dis[MAXN], vis[MAXN], tot;
void addedge(int u, int v, int c) {
	edge[tot] = (Edge) { v, c, head[u] };
	head[u] = tot++;
	edge[tot] = (Edge) { u, 0, head[v] };
	head[v] = tot++;
}
bool bfs(int s, int t) {
	int he = 0, ta = 0;
	memset(dis, -1, sizeof(dis));
	dis[que[ta++] = s] = 0;
	while (he < ta) {
		int u = que[he++];
		for (int i = head[u]; ~i; i = edge[i].next) {
			int v = edge[i].to;
			if (edge[i].cap && dis[v] < 0) {
				dis[que[ta++] = v] = dis[u] + 1;
				if (v == t) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int t, int f) {
	if (u == t) return f;
	int flow = 0;
	for (int i = head[u]; ~i; i = edge[i].next) {
		int v = edge[i].to;
		if (edge[i].cap > 0 && dis[v] > dis[u]) {
			int d = dfs(v, t, min(f, edge[i].cap));
			if (d == 0) continue;
			f -= d, flow += d;
			edge[i].cap -= d, edge[i ^ 1].cap += d;
			if (f == 0) return flow;
		}
	}
	if (flow == 0) dis[u] = -1;
	return flow;
}
int dinic(int s, int t) {
	int flow = 0;
	while (bfs(s, t)) flow += dfs(s, t, INF);
	return flow;
}
int A[MAXN], B[MAXN], C[MAXN], f[MAXN], arr[MAXN], n, T;
bool cmp(int x, int y) { return C[x] < C[y]; }
void cut(int x) {
	for (int i = head[x * 2 - 1]; ~i; i = edge[i].next)
		if (edge[i].to == x * 2) edge[i].cap = edge[i ^ 1].cap = 0;
}
int main() {
	for (read(T); T--;) {
		read(n);
		memset(head, -1, sizeof(head)); tot = 0;
		for (int i = 1; i <= n; i++) read(A[i]);
		for (int i = 1; i <= n; i++) read(B[i]);
		for (int i = 1; i <= n; i++) read(C[i]);
		int s = n * 2 + 1, t = s + 1, mx = 0;
		for (int i = 1; i <= n; i++) {
			f[i] = 1;
			for (int j = 1; j < i; j++)
				if (A[j] < A[i]) f[i] = max(f[i], f[j] + 1);
			for (int j = 1; j < i; j++)
				if (A[j] < A[i] && f[j] + 1 == f[i]) addedge(j * 2, i * 2 - 1, INF);
			addedge(i * 2 - 1, i * 2, B[i]);
			if (f[i] == 1) addedge(s, i * 2 - 1, INF);
			mx = max(mx, f[i]);
		}
		for (int i = 1; i <= n; i++) if (f[i] == mx) addedge(i * 2, t, INF);
		print(dinic(s, t), ' ');
		for (int i = 1; i <= n; i++) arr[i] = i;
		sort(arr + 1, arr + 1 + n, cmp);
		vector<int> ans;
		for (int i = 1; i <= n; i++) {
			int x = arr[i];
			if (bfs(x * 2 - 1, x * 2)) continue;
			cut(x), dinic(x * 2 - 1, s), dinic(t, x * 2);
			ans.push_back(x);
		}
		print(ans.size());
		sort(ans.begin(), ans.end());
		for (int i : ans) print(i, ' ');
		printc('\n');
	}
	ioflush();
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值