【学习笔记】[AGC043F] Jewelry Box

发布时间:2024年01月15日

点击看题意

Part 1

前置知识: L P LP LP对偶费用流。

考虑这样一个费用流:每条边 u v uv uv的流量设为 f u v f_{uv} fuv?,容量设为 c u v c_{uv} cuv?,费用设为 w u v w_{uv} wuv? b u b_u bu?设为流出-流入。要求 m i n i m i z e ∑ f u v w u v {\rm minimize} \sum f_{uv}w_{uv} minimizefuv?wuv?。通常来讲,如果 b u > 0 b_u>0 bu?>0则从源点 S S S补流过来,否则流向汇点 T T T,因此这是一个最小费用最大流问题(假设原图没有负环)。

经过 一番操作 ,可以发现对偶过后长这样:

m i n i m i z e ∑ u b u p u + ∑ u v c u v max ? ( 0 , p v ? p u ? w u v ) {\rm minimize}\sum_{u}b_up_u+\sum_{uv}c_{uv}\max(0,p_v-p_u-w_{uv}) minimizeu?bu?pu?+uv?cuv?max(0,pv??pu??wuv?)

注意,两者对应的答案互为相反数,因此提取答案时要取负。

通常来讲,后面的式子一般出现在含差分约束的式子中。而为了减少一条约束中变量的数量,我们通常需要转化为前缀和。

PS:建议 b u b_u bu?不要像这篇博客一样写成流入-流出,否则后面推出来怪怪的。。。

Part 2

首先根据贪心,一定是将一个商店里的 A A A个珠宝按重量从小到大排序,然后将每个商店中对应第 i i i大的珠宝放在一起,如果这样都不行的话就不合法。

进一步的,考虑设 x i , j x_{i,j} xi,j?表示第 i i i个商店选的编号 ≤ j \le j j的珠宝的数量(注意一定要把 i = 0 i=0 i=0的变量也建出来),那么对于一条限制 ( u , v , w ) (u,v,w) (u,v,w) ? i ∈ [ 1 , K v ] \forall i\in [1,K_v] ?i[1,Kv?],设 j j j是最小的满足 S u , j + w ≥ S v , i S_{u,j}+w\ge S_{v,i} Su,j?+wSv,i?的数,则 x u , j ? 1 ≤ x v , i ? 1 x_{u,j-1}\le x_{v,i-1} xu,j?1?xv,i?1?

现在写出问题对应的线性规划形式:

m i n i m i z e ∑ x i , j ( P i , j ? P i , j + 1 ) s . t . ??? x i , j ? x i , j + 1 ≤ 0 x i , j + 1 ? x i , j ? C i , j + 1 ≤ 0 x u , k ? x v , j ≤ 0 x i , K i ? x i , 0 ? A ≤ 0 x i , 0 ? x i , K i + A ≤ 0 x i , 0 = x j , 0 x i , j ≥ 0 {\rm minimize} \sum x_{i,j}(P_{i,j}-P_{i,j+1})\\ s.t.\ \ \ x_{i,j}-x_{i,j+1}\le 0\\x_{i,j+1}-x_{i,j}-C_{i,j+1}\le 0\\ x_{u,k}-x_{v,j}\le 0\\ x_{i,K_i}-x_{i,0}-A\le 0\\ x_{i,0}-x_{i,K_i}+A\le 0\\ x_{i,0}=x_{j,0}\\ x_{i,j}\ge 0 minimizexi,j?(Pi,j??Pi,j+1?)s.t.???xi,j??xi,j+1?0xi,j+1??xi,j??Ci,j+1?0xu,k??xv,j?0xi,Ki???xi,0??A0xi,0??xi,Ki??+A0xi,0?=xj,0?xi,j?0

现在对于每条差分约束 x ? y ≤ w x-y\le w x?yw,都连一条 ( y , x , ∞ , w ) (y,x,\infty,w) (y,x,,w)的边,然后根据 P i , j ? P i , j + 1 P_{i,j}-P_{i,j+1} Pi,j??Pi,j+1?的正负建立源汇点平衡流量,答案就是对应的最小费用最大流。

但是这样无法处理多组询问。注意到原图上存在 x i , j x_{i,j} xi,j? x i , j ? 1 x_{i,j-1} xi,j?1?的容量为 ∞ \infty ,费用为 0 0 0的边,而 x i , j x_{i,j} xi,j?的流出量为 P i , j ? P i , j + 1 P_{i,j}-P_{i,j+1} Pi,j??Pi,j+1?,因此考虑先在 x i , j x_{i,j} xi,j? x i , j ? 1 x_{i,j-1} xi,j?1?这条边流大小为 P i , j P_{i,j} Pi,j?的流量,然后连一条 ( x i , j ? 1 , x i , j , P i , j , 0 ) (x_{i,j-1},x_{i,j},P_{i,j},0) (xi,j?1?,xi,j?,Pi,j?,0)的边表示可以反悔,不难发现这样每条边都自动流量平衡了。这样我们将问题转化为了最小费用可行流,即不断找负环增广,直到代价 > 0 >0 >0

现在,注意到整张图实际上只有 x i , K i x_{i,K_i} xi,Ki?? x i , 0 x_{i,0} xi,0?这一条负边,因此这条边一定会被增广,这实际上就是从 x 0 , j x_{0,j} x0,j? x i , K i x_{i,K_i} xi,Ki??的最小回归代价减去 A A A(注意 j j j是任意的,这是 x i , 0 = x j , 0 x_{i,0}=x_{j,0} xi,0?=xj,0?建出来的边),所以如果将所有 x 0 , j x_{0,j} x0,j?看成源点 S S S x i , K i x_{i,K_i} xi,Ki??看成汇点 T T T,答案就是从 S S S T T T不断增广,直到代价 > A >A >A时的答案。

注意到代价为 A A A的边其实是废边,所以这张图实际上和 A A A没关系了。又因为费用流具有凸性,因此二分即可。

怎么判无解?有解等价于差分约束有解,也就是说没有负环(这里指容量为 ∞ \infty 的边),而有负环的时候总流量一定为 ∞ \infty

大力冲费用流即可。大概要跑 ∑ P i , j \sum P_{i,j} Pi,j?次最短路。

#include<bits/stdc++.h>
#define fi first
#define se second
#define inf 0x3f3f3f3f3f3f3f3f
#define int ll
#define ll long long
using namespace std;
const int N=114514;
const int M=114514;
int n,m,s,t,idx; 
int head[M],nxt[M],to[M],cap[M],cost[M],tot=1;
queue<int>Q;
int dis[N],pre[N],vis[N];
void add(int u,int v,int w,int c){
    tot++,to[tot]=v,nxt[tot]=head[u],head[u]=tot,cap[tot]=w,cost[tot]=c;
    tot++,to[tot]=u,nxt[tot]=head[v],head[v]=tot,cap[tot]=0,cost[tot]=-c;
}
bool spfa(){
    for(int i=1;i<=idx;i++)dis[i]=inf,vis[i]=0;
    dis[s]=0;
    Q.push(s);
    vis[s]=1;
    while(Q.size()) {
        int u=Q.front();Q.pop();
        vis[u]=0;
        for(int k=head[u];k;k=nxt[k]){
            int v=to[k];
            if(cap[k]>0&&dis[u]+cost[k]<dis[v]) {
                dis[v]=dis[u]+cost[k];
                pre[v]=k;
                if(!vis[v]) {
                    vis[v]=1;
                    Q.push(v);
                }
            }
        }
    }
    return dis[t]!=inf;
}
int cnt;
ll f[N],sm1[N],sm2[N];
void EK(){
    while(spfa()){
        int now=t,flo=inf;
        while(now!=s){
            int k=pre[now];
            flo=min(flo,cap[k]);
            now=to[k^1];
        }
        f[++cnt]=dis[t],sm1[cnt]=sm1[cnt-1]+flo,sm2[cnt]=sm2[cnt-1]+flo*dis[t];
        if(sm1[cnt]>inf)break;
        now=t;
        while(now!=s){
            int k=pre[now];
            cap[k]-=flo;
            cap[k^1]+=flo;
            now=to[k^1];
        }
    }
}
int sz[35],id[35][35];
struct node{
    int s,p,c;
    bool operator <(const node &r)const{
        return s<r.s;
    }
}a[35][35];
signed main(){
    //freopen("data.in","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n,s=++idx,t=++idx;
    for(int i=1;i<=n;i++){
        cin>>sz[i];
        for(int j=1;j<=sz[i];j++)cin>>a[i][j].s>>a[i][j].p>>a[i][j].c;
        sort(a[i]+1,a[i]+1+sz[i]);
        for(int j=1;j<sz[i];j++)id[i][j]=++idx;
        id[i][0]=s,id[i][sz[i]]=t;
        for(int j=0;j<sz[i];j++){
            add(id[i][j],id[i][j+1],inf,a[i][j+1].c);
            add(id[i][j],id[i][j+1],a[i][j+1].p,0);
        }
    }
    cin>>m;
    for(int i=1;i<=m;i++){
        int u,v,w;cin>>u>>v>>w;
        int k=1;
        for(int j=1;j<=sz[v];j++){
            while(k<=sz[u]&&a[v][j].s>a[u][k].s+w)k++;
            add(id[v][j-1],id[u][k-1],inf,0);
        }
    }
    EK();
    int _Q;
    cin>>_Q;
    while(_Q--){
        int x;cin>>x;
        if(x>f[cnt]){
            cout<<-1<<"\n";
        }
        else{
            int p=lower_bound(f+1,f+1+cnt,x)-f-1;
            cout<<x*sm1[p]-sm2[p]<<"\n";
        }
    }
}
文章来源:https://blog.csdn.net/cqbzlydd/article/details/135608086
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。