首页 > 其他分享 >P3749 题解

P3749 题解

时间:2024-03-01 22:34:27浏览次数:26  
标签:int 题解 rep dep add edge P3749 id

P3749 [六省联考 2017] 寿司餐厅 题解

发现很少有人讲为什么这题是最大权闭合子图,但作为一个刚学网络流的蒟蒻,我认为考虑是必要的。

最大权闭合子图的特点:

  • 存在单向依赖关系,选 \(x\) 必须选 \(y\)。
  • 每个点只会被选一次。
  • 代价有正有负。

本问题特点:

  • 选一个区间,必选所有子区间(单向依赖)。
  • 贡献 & 代价都只算一次。
  • 有正有负。

完美符合要求!

只需要打个补丁,选 \([i,i]\) 必选 \(a_i\)。


会最大权闭合子图的可以跳过下面一部分。

最大权闭合子图模型

考虑最优为选所有正的,不选负的。

建图:

  • \(s\) 为源点,\(t\) 为汇点。
  • 对于依赖关系 \(x\to y\),连边 \((x,y,INF)\)。
  • 对于节点,若代价为正,连边 \((s,i,v_i)\);反之,连边 \((i,t,-v_i)\)。

若割掉与 \(s\) 相连的边,代表不选这个正的,损失 \(v_i\) 贡献;若割掉与 \(t\) 相连的边,代表选这个负的,付出 \(v_i\) 代价。

发现 \(s\) 和 \(t\) 只会通过形如 \(s\to x\to y\to t\) 的边相连。

  • 如果保留 \((y,t)\),即选 \(y\),则必然割掉 \((s,x)\),即不选 \(x\)。
  • 如果割掉 \((y,t)\),即不选 \(y\),\((s,x)\) 可割可不割,不受影响。

故此图的割满足选 \(y\) 是选 \(x\) 的必要条件

最大权为 所有正权值之和 \(-\) 最小割。


讲到这里本题建图就很简单了,不懂的请自行看代码理解。

#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define REPG(i,g,x) for(int i=g.head[x];~i;i=g.edge[i].nxt)

//此处省略快读板子

const int N=1e5+5,M=2e6+5;
struct graph{
    int head[N],cnt;
    struct node{
        int to,nxt,w,c;
    }edge[M];
    inline void add(int x,int y,int w,int c){
        edge[++cnt]=(node){y,head[x],w,c};head[x]=cnt;
    }
    inline void clear(){
        memset(head,-1,sizeof(head));cnt=1;
    }
}g;
int n,m,s,t;
const int NF=105;
const int INF=1e9+5;
int a[NF],d[NF][NF],id[NF][NF];
int Maxa;

namespace Dinic{
    int dep[N],cur[N];
    queue<int> q;
    bool bfs(){
        memset(dep,-1,sizeof(dep));
        memcpy(cur,g.head,sizeof(g.head));
        while(!q.empty()) q.pop();
        
        dep[s]=1;q.push(s);
        while(!q.empty()){
            int u=q.front();q.pop();
            if(u==t) break;
            REPG(i,g,u){
                int v=g.edge[i].to,w=g.edge[i].w,c=g.edge[i].c;
                if(dep[v]==-1 && w>c){
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[t]!=-1;
    }
    int dfs(int u,int f){
        if(u==t || !f) return f;
        int res=0;
        for(int i=cur[u];(~i) && f!=res;i=g.edge[i].nxt){
            int v=g.edge[i].to,w=g.edge[i].w,c=g.edge[i].c;
            cur[u]=i;
            if(dep[v]==dep[u]+1 && w>c){
                int nf=dfs(v,min(f-res,w-c));
                res+=nf;
                g.edge[i].c+=nf;
                g.edge[i^1].c-=nf;
            }
        }
        return res;
    }
    int solve(){
        int ans=0;
        while(bfs()) ans+=dfs(s,INF);
        return ans;
    }
}

int main(){
    g.clear();
    read(n),read(m);
    rep(i,1,n) read(a[i]),Maxa=max(Maxa,a[i]);
    s=1,t=2;
    int tot=2;
    rep(i,1,n) rep(j,i,n){
        read(d[i][j]);
        id[i][j]=++tot;
    }
    int mx=0;
    rep(i,1,n) rep(j,i,n){
        if(i==j){
            //选 d[i][i] 必选 a[i]
            g.add(id[i][j],tot+a[i],INF,0);
            g.add(tot+a[i],id[i][j],0,0);
            //选 d[i][i] 需要付出 a[i] 的代价
            d[i][j]-=a[i];
        }
        else{
            //选 d[i][j] 必选 d[i+1][j],d[i][j-1]
            g.add(id[i][j],id[i+1][j],INF,0);
            g.add(id[i+1][j],id[i][j],0,0);
            g.add(id[i][j],id[i][j-1],INF,0);
            g.add(id[i][j-1],id[i][j],0,0);
        }
        if(d[i][j]>0) g.add(s,id[i][j],d[i][j],0),g.add(id[i][j],s,0,0),mx+=d[i][j];
        else g.add(id[i][j],t,-d[i][j],0),g.add(t,id[i][j],0,0);
    }
    //选 i 需要付出 m*i*i 的代价
    rep(i,1,Maxa) g.add(tot+i,t,m*i*i,0),g.add(t,tot+i,0,0);
    printf("%d\n",mx-Dinic::solve());
    return 0;
}

标签:int,题解,rep,dep,add,edge,P3749,id
From: https://www.cnblogs.com/Cindy-Li/p/18048100

相关文章

  • ABC338G 题解
    ABC338G题解计数题,没有太多思维难度,就是麻烦。显然+和*是比较难搞的,应考虑子问题。复杂度要求线性,考虑每个位置的贡献。Case1:只有数字Ex:1234考虑2的贡献,枚举一下看看。\(12=1\times10+2\times1\)\(123=1\times100+2\times10+3\times1\)\(1234=\dots\)\(2=2......
  • [ABC217F] Make Pair 题解
    [ABC217F]MakePair题解思路解析通过\(n\le200\)和“选出的两个学生离开队列,空出来的位置左右合拢”这两个细节可以想到能用区间dp做,\(f_{i,j}\)表示将\(i\toj\)这个区间全部选完的方案数,然后常规区间dp,加一个判断如果当前区间\([l,r]\)中\(l,r\)是朋友,就可......
  • P4690 [Ynoi2016] 镜中的昆虫 题解
    题目链接:镜中的昆虫经典题了,我们首先回顾下颜色数的常见做法统计:对每个位置维护一个\(pre_i\),表示与当前位置相同的颜色上一次出现位置。那么我们分讨一下。查询\([l,r]\)得到颜色数,对于\(pre_i<l\)的\(i\)点,显然它就是这个区间内\(a_i\)对应颜色出现的第一个位置,我们......
  • window.open 循环下载多个文件会打开新页签问题解决
     批量下载文件,循环使用window.open(url)的方式会打开新页签,参考了一位大侠的文章,使用iframe可以的:https://blog.csdn.net/nanke_yh/article/details/125145717如下:fileList.forEach(file=>{//同时下载多个文件,使用iframe下载,因为window.open或者a......
  • $\text{20240301}$ 字符串练习题解
    \(\text{20240301}\)字符串练习题解一定要写冬令营的题吗?遗憾的。P9717给了一个\(n\)个数的首尾相接的字符串,求若干个操作后能形成的不同的字符串大小。一次操作定义为:将字符串内所有的\(\text{01}\)同时改成\(\text{10}\),如图。通过这张图我们似乎发现了一个规律,这......
  • CF1937D Pinball 题解
    题目链接:https://codeforces.com/contest/1937/problem/D题意简述一个长为n的格子,用'<'或'>'组成的字符串表示,在位置i放一个小球,当前所在位置是'<'则下一秒左移一步,否则下一秒右移一步。小球移动后,之前位置的符号反转,'<'变成'>','>'变成'<',直到小球离开整个......
  • [CF1804F] Approximate Diameter 题解
    题目链接题目分析显然图结构不太好维护,容易想到维护树结构。维护生成树看起来就不太靠谱,容易想到维护最短路树。keyobservation:我们固定一个端点(不妨为\(1\)),求出这个点到每个点的最短路长度的最大值\(x\)。则一定有\(\lceil{d\over2}\rceil\lex\led\)。证明:显然\(x\l......
  • CF1827C Palindrome Partition 题解
    CF1827CPalindromePartition题解题面题目传送门。称一个字符串是好的,当且仅当它是一个长度为偶数的回文串或由若干长度为偶数的回文串拼接而成。给定一个长度为\(n\)的字符串\(s\),求有多少\(s\)的子串是好的。$1\len\le5\cdot10^5$,\(s\)仅包含小写字母。......
  • P6185 序列 题解
    如果发现自己莫名其妙错了,可能是代码UB,还开O2!!!!!!!!!!!传送门首先,对于每个操作2,将\(u_i,v_i\)连边。连边之后每个连通块内部可以在总和不变的情况下任意改变。用并查集将每个连通块缩点,然后对于每个操作1,将\(u_i,v_i\)连边。得到的图又会分成若干个连通块。判断整个图是否可行,只......
  • [ABC238F] Two Exams 题解
    [ABC238F]TwoExams题解思路解析这题很麻烦,因为有两个维度。所以可以想到先按照第一维排序,这样就不需要考虑第二维的问题。其次再发现数据范围小,可以想到能用dp做,接下来就考虑如何dp。首先我们要知道我们遍历到了第几个公民,同时还要知道还剩下几个代表名额,同时我们还需要思......