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;
}