AT4695 [AGC031E] Snuke the Phantom Thief(费用流)

费用流神仙题!

题目链接:AT4695 [AGC031E] Snuke the Phantom Thief

题意简述:给一个\(N*M的\)网格图,上面有\(n\)个珠宝\((x,y,v)\),\(v\)是价值,\((x,y)\)是坐标,给若干个限制,每个限制形式如下

  • 横坐标 \(\leq a_i\) 的点不能超过\(b_i\)
  • 横坐标 \(\geq a_i\) 的点不能超过\(b_i\)
  • 纵坐标 \(\leq a_i\) 的点不能超过\(b_i\)
  • 纵坐标 \(\geq a_i\) 的点不能超过\(b_i\)

求在满足以上限制条件下,能取的珠宝价值最大是多少.

\(N,M,x,y \leq 100,n \leq 80,v \leq 1e15\)

solution:

  • 看到很小的范围和奇怪的限制考虑网络流
  • 发现不确定总共选了多少个珠宝很难算,我们考虑枚举选了\(k\)个珠宝(也是为了方便跑费用流)
  • 然后转化题意变成了形如:第\(b_{i+1}\)个点横坐标要\(> a_i\),第\(k - b_{i}\)个点横坐标要\(< a_i\).
  • 考虑限制如今是什么,考虑构建三类点,一类是代表按答案中横坐标排序第\(rk\)的点,一类是珠宝,一类是代表答案中按纵坐标排序第\(rk\)的点,那么我们可以给对应的珠宝连上限制.
  • \(example\):(此处仅考虑横坐标,纵坐标同理),若\(A\)表示答案中横坐标排序\(b_i\)个点(按横坐标排序)要\(> a_i\)(这里指的是综合了所有条件以后,事实上一般是一个\([l,r]\)区间),那么把横坐标\(> a_i\)的珠宝都连向\(A\).
  • 每个珠宝拆两个点,出点入点来限制每个珠宝只能被选一次,连两条边,入点连代表横坐标的点,出点连代表纵坐标的点.费用可以任意放在其中一个上面.代表横坐标的点可以向\(S\)连边,代表纵坐标的点可以向\(T\)连边.跑费用流即可.
/*[AGC031E] Snuke the Phantom Thief*/ 
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
ll read(){
	char c = getchar();
	ll x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
const int N = 1e5 + 7;
struct Edge{
	int nxt,point,flow,cost;
}edge[N<<1]; 
int head[N],S,T,tot = 1,n;
void add_edge(int u,int v,int f,int c){
//	cout<<u<<' '<<v<<' '<<f<<' '<<c<<endl;
	edge[++tot].nxt = head[u];edge[tot].point = v;head[u] = tot;edge[tot].flow = f;edge[tot].cost = c;
	edge[++tot].nxt = head[v];edge[tot].point = u;head[v] = tot;edge[tot].flow = 0;edge[tot].cost = -c;
}
ll ans = 0;
ll dis[N];int cur[N];bool vis[N];
struct Limit{
	int op,a,b;
}lim[N];
int R;
ll mxcost;
struct Node{
	int x,y,v,id;
	Node (int _a = 0,int _b = 0,int _c = 0,int _d = 0){
		x = _a,y = _b,v = _c,id = _d;
	}
}p[N];
bool cmpx(Node a,Node b){
	return a.x < b.x; 
}
bool cmpy(Node a,Node b){
	return a.y < b.y;
}
int id[2][N],_x[N],_y[N],q[N];
int Lx[N],Rx[N],Ly[N],Ry[N];
bool spfa(){
	for(int i = S; i <= T; ++i)	vis[i] = 0,cur[i] = head[i],dis[i] = -1e18;
	dis[S] = 0;int l = 1,r = 0;q[++r] = S;
	while(l <= r){
		int x = q[l++];vis[x] = 0; 
		for(int i = head[x]; i ; i = edge[i].nxt){
			int y = edge[i].point;
			if(dis[y] < dis[x] + edge[i].cost && edge[i].flow){
				dis[y] = dis[x] + edge[i].cost;
				if(!vis[y])		q[++r] = y,vis[y] = 1;
			}
		}
	}
	return dis[T] >= 0;
}
int dinic(int x,int flow){
	if(!flow || x == T)	return flow;
	int rest = flow,k;vis[x] = 1;
	for(int i = cur[x]; i ; i = edge[i].nxt){
		int y = edge[i].point;
		cur[x] = i;
		if(!vis[y] && edge[i].flow && dis[y] == dis[x] + edge[i].cost){
			k = dinic(y,min(rest,edge[i].flow));
			if(!k)	dis[y] = -1;
			if(rest == 0)	return flow;
			edge[i].flow -= k;
			edge[i^1].flow += k;
			mxcost += k * edge[i].cost;
			rest -= k;
		}
	}	
	vis[x] = 0;
	return flow - rest;
}
void solve(int k){
	for(int i = S; i <= T; ++i)		head[i] = 0;
	tot = 1;
	S = 0;T = 2 * (k + n) + 1;
	for(int i = 1; i <= n; ++i)		id[0][i] = i + 2 * k,id[1][i] = id[0][i] + n;
	for(int i = 1; i <= n; ++i)		add_edge(id[0][i],id[1][i],1,p[i].v);
	for(int i = 1; i <= k; ++i)		_x[i] = i,add_edge(S,_x[i],1,0);
	for(int i = 1; i <= k; ++i)		_y[i] = i + k,add_edge(_y[i],T,1,0);
	for(int i = 1; i <= n; ++i)		Lx[i] = 1,Rx[i] = 100,Ly[i] = 1,Ry[i] = 100;
//	sort(p+1,p+n+1,cmpx);
	for(int i = 1; i <= R; ++i){
		if(lim[i].op == 0) {
			if(lim[i].b + 1 <= k)	
				Lx[lim[i].b + 1] = max(Lx[lim[i].b + 1],lim[i].a + 1);
		}
		if(lim[i].op == 1){
			if(k - lim[i].b >= 1)
				Rx[k-lim[i].b] = min(Rx[k-lim[i].b],lim[i].a - 1);
		}
		if(lim[i].op == 2){
			if(lim[i].b + 1 <= k)
				Ly[lim[i].b + 1] = max(Ly[lim[i].b + 1],lim[i].a + 1);
		}
		if(lim[i].op == 3){
			if(k - lim[i].b >= 1)
				Ry[k-lim[i].b] = min(Ry[k-lim[i].b],lim[i].a - 1);
		}
	}
	for(int i = 1; i < k; ++i)		Lx[i+1] = max(Lx[i],Lx[i+1]),Ly[i+1] = max(Ly[i],Ly[i+1]);
	for(int i = k; i > 1; --i)		Rx[i-1] = min(Rx[i],Rx[i-1]),Ry[i-1] = min(Ry[i],Ry[i-1]);
	for(int i = 1; i <= k; ++i){/*x*/
		for(int j = 1; j <= n; ++j){
			if(Lx[i] <= p[j].x && Rx[i] >= p[j].x){
				add_edge(_x[i],id[0][j],1,0);
			}
			if(Ly[i] <= p[j].y && Ry[i] >= p[j].y){
				add_edge(id[1][j],_y[i],1,0);
			}
		}
	}
	mxcost = 0;
	int mxflow = 0;
	while(spfa()){
		mxflow += dinic(S,1e9);
//		cout<<mxflow<<endl;
	}
	if(mxflow == k){
		ans = max(ans,mxcost);
	}
}
signed main(){
	n = read();
	for(int i = 1; i <= n; ++i){
		p[i].x = read(),p[i].y = read(),p[i].v = read();p[i].id = i;
	}
	R = read();
	char opt[15];
	for(int i = 1; i <= R; ++i){
		scanf("%s",opt);
		if(opt[0] == 'L')	lim[i].op = 0;
		if(opt[0] == 'R')	lim[i].op = 1;
		if(opt[0] == 'D')	lim[i].op = 2;
		if(opt[0] == 'U')	lim[i].op = 3;
		lim[i].a = read();lim[i].b = read();
	}
	for(int i = 1; i <= n; ++i)		solve(i);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-03-16 22:07  y_dove  阅读(145)  评论(0编辑  收藏  举报