[BZOJ3532][Sdoi2014]Lis && 最小字典序割

最小代价一看就是拆点最大流 问题在于如何求字典序的方案

如何判断一条边是否是割边是很容易的 只需要从u出发看是否能找到一条u到v的增广路,如果存在这样的一条路径 说明该边不是割边

那么我们按照C的大小来枚举每一条边 如果该边是割边 那么我们就要把它从图中删除(这样可以让一些割边不再是满流的边 这些删除的割边都是一定不与当前割边在同一割集中的割边 要让这样的边不能入选)

那么我们要怎样最快的消除这条边呢?我们使用退流算法 从T到v跑一次最大流 从u到S跑一遍最大流即可消除当前边的影响 再把当前边的容量设置为0

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define SF scanf
#define PF printf
#define mp make_pair
#define fir first
#define sec second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 700;
const int MAXM = (MAXN * MAXN / 2) + MAXN;
const int INF = 0x3f3f3f3f;;
struct Node {
	int u, v, next, c;
} ;
struct ISAP {
	Node Edge[MAXM*2+10];
	int adj[MAXN*2+10], gap[MAXN*2+10], d[MAXN*2+10], ecnt;
	int n, m, s, t;
	void addedge(int u, int v, int c) {
		Node &e = Edge[ecnt];
		e.u = u; e.v = v; e.c = c; e.next = adj[u];
		adj[u] = ecnt++;
	}
	void add(int u, int v, int c) {
		addedge(u, v, c); addedge(v, u, 0);
	}
	int aug(int u, int inc) {
		int mindis = n-1, Inc = 0;
		if(u == t) return inc;
		for(int i = adj[u]; ~i; i = Edge[i].next) {
			Node &e = Edge[i];
			int v = e.v, c = e.c;
			if(c) {
				if(d[v] == d[u]-1) {
					int del = min(inc-Inc, c);
					del = aug(v, del);
					Inc += del;
					Edge[i].c -= del;
					Edge[i^1].c += del;
					if(d[s] >= n) return Inc;
					if(Inc == inc) return Inc;
				}
				mindis = min(mindis, d[v]);
			}
			
		}
		if(!Inc) {
			gap[d[u]]--;
			if(!gap[d[u]]) d[s] = n;
			d[u] = mindis+1;
			
			gap[d[u]]++;
		}
		return Inc;
	}
	void init_dis() {
		queue <int> q;
		memset(d, 0, sizeof(d));
		memset(gap, 0, sizeof(gap));
		q.push(t);
		while(!q.empty()) {
			int u = q.front(); q.pop();
			gap[d[u]]++;
			for(int i = adj[u]; ~i; i = Edge[i].next) {
				int v = Edge[i].v;
				if(!d[v] && v != t) {
					d[v] = d[u] + 1; q.push(v);
				}
			}
		}
	}
	int MaxFlow(int _s, int _t, int _n) {
		s = _s; t = _t; n = _n;
		int Flow = 0;
		init_dis();
		while(d[s] < n) Flow += aug(s, INF);
		return Flow;
	}
	void init() {
		memset(adj, -1, sizeof(adj));
		ecnt = 0;
	}
	bool bfs(int x, int y) {
		queue <int> q;
		memset(d, 0, sizeof(d));
		d[x] = 1; q.push(x);
		while(!q.empty()) {
			int u = q.front(); q.pop();
			for(int i = adj[u]; ~i; i = Edge[i].next) {
				int v = Edge[i].v;
				if(!d[v] && Edge[i].c) d[v] = 1, q.push(v);
			}
		}
		return d[y];
	}
} sap;
int A[MAXN+10], f[MAXN+10], B[MAXN+10], n, MAX, S, T;
pii C[MAXN+10];
void dp() {
	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);
		MAX = max(MAX, f[i]);
	}
}
void Build_Graph() {
	for(int i = 1; i <= n; i++) sap.add(i, n+i, B[i]);
	for(int i = 1; i <= n; i++) {
		if(f[i] == 1) sap.add(S, i, INF);
		if(f[i] == MAX) sap.add(n+i, T, INF);
		for(int j = i+1; j <= n; j++)
			if(f[j] == f[i]+1 && A[j] > A[i])
				sap.add(n+i, j, INF);
	}
}
vector <int> ans;
int main() 
{
	int _T; SF("%d", &_T); while(_T--) {
		sap.init();
		ans.clear();
		MAX = 0;
		SF("%d", &n);
		S = 2*n+1; T = S+1;
		for(int i = 1; i <= n; i++) SF("%d", &A[i]);
		for(int i = 1; i <= n; i++) SF("%d", &B[i]);
		for(int i = 1; i <= n; i++) SF("%d", &C[i].fir), C[i].sec = i;
		dp();
		Build_Graph();
		int mincost = sap.MaxFlow(S, T, T);
		sort(C+1, C+1+n);
		for(int i = 1; i <= n; i++) {
			int x = C[i].sec;
			if(sap.Edge[(x-1)*2].c || sap.bfs(x, x+n)) continue;
			ans.push_back(x);
			sap.MaxFlow(x, S, T); sap.MaxFlow(T, x+n, T);
			sap.Edge[(x-1)*2^1].c = sap.Edge[(x-1)*2].c = 0;
		}
		PF("%d %d\n", mincost, (int)ans.size());
		sort(ans.begin(), ans.end());
		for(int i = 0; i < ans.size(); i++)
			PF("%d%c", ans[i], (i == ans.size()-1) ? '\n' : ' ');
	}
}
/*
2
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值