1 条题解

  • 0
    @ 2025-8-24 21:48:06

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 皎月半洒花
    在那之前,你要多想。

    搬运于2025-08-24 21:48:06,当前版本为作者最后更新于2020-03-18 08:31:10,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    感觉此题大部分题解都一点也不负责任。

    本人十分不赞同,对于一道网络流题,一篇题解只说怎么建图不说思路的行为。我就想请问,难道做一道题的目的,就只在于做这一道题吗?A了就好?原理思路什么爱管不管?这能叫题解?

    网络流题的重点就是在于建图。如果这都能一笔带过,那还做个锤子?

    毫不夸张的说,如果张三做网络流题的时候就这么似是而非,就算他勤勤恳恳地做完了24题,省选的时候也运气好遇到了网络流题,做出来的概率绝对不高,或者说就根本不可能做出来。

    你说,网络流题是什么?到现在为止还只是一个靠经验和阅历通杀的题目类别。所以网络流题需要格外重视经验的积累,知其然知其所以然。

    总之呢,一道网络流题,不仔细分析思路直接告诉你怎么建图,就是在耍流氓。


    给定实直线 L L n n 个开区间组成的集合 I I ,和一个正整数 k k ,试设计一个算法,从开区间集合 I I 中选取出开区间集合 SI S \subseteq I ,使得在实直线 L L 的任何一点 x x S S 中包含点 x x 的开区间个数不超过 k k 。且 zSz \sum\limits_{z \in S} | z | 达到最大。这样的集合 S S 称为开区间集合 I I 的最长 k k 可重区间集。zSz \sum\limits_{z \in S} | z | 称为最长 k k 可重区间集的长度。

    对于给定的开区间集合 I I 和整数 k k ,计算开区间集合 I I 的最长 k k 可重区间集的长度。

    1n500,1k3 1 \leq n \leq 500, 1 \leq k \leq 3

    首先一拿出来,这不就是匹配问题嘛?一个点最多匹配 kk 个区间。于是每个位置建一个点,然后连向覆盖自己的点,然后…好像不太对?其一他没给下标的取值范围,其二一个线段覆盖多个点,要么都覆盖要么都不覆盖,这个限制很难表示…

    于是好像不知道从何处入手了。发现一个这样的性质,就是永远不会选择 kk 个以上交于 11 点的区间。也就是说,如果两个区间彼此之间没有交,就可以同时选;否则能不能同时选,看情况。这像极了「限制」,也就是如果两个区间之间没有交,那两者不存在限制;否则存在 kk 的限制。

    根据一开始的匹配,可以猜到大致上用网络流是可行的。并且似乎网络流很适合用流量来表征限制。那么考虑,如果两个区间不存在限制,那么应该怎么办——网络流类似电流,所以此时如果串联的话,就代表着可以同时选;那么如果存在限制,就意味着不能串联。根据这一点,考虑如何串联。发现本质上是将两个不交的区间中间连 f=,c=0f=\infty,c=0 的边。

    这一点就引申出两个建图方法,其本质是相同的:

    1、建立一个超级源 S\rm S 和一个源 S\rm S' ,中间连 f=k,c=0f=k,c=0 的边,目的是提供初始流量。S\rm S' 向每个区间的左端点连一条 f=1,c=1f=1,c=-1 边。然后区间左端点向右端点连边 f=1,c=lenf=1,c=-len 表示贡献,每个右端点再向 T\rm T 连边即可。如果两个区间不交,就由一个区间的 rr 连向另一个区间的 ll (当然要按秩啦)。思考这样做的合理性,对于相交的区间,一定是并联;否则的话就是串联(其实叫做混连,但是问题不大)。

    2、建立一个源 S\rm S 连向数轴上的 00 位置,f=k,c=0f=k,c=0。然后数轴上每个 i>0i>0i+1i+1 连边 f=k,c=0f=k,c=0。最后 maxrightmaxrightT\rm T 连边。对于一个区间,连法跟1相同。

    注意:

    1、为什么要拆点?此处拆点的作用值得注意。对于一个区间,本质上应该抽象成一个点。但是在流图里是不存在「点权」这个概念的。所以需要把点权转边权,拆点的作用便在于此。

    2、其实上面两个方法,可以通过初中物理里面什么「判断两个电路图是否等价」的知识来解决的233

    3、由于本题保证了「开区间」,所以可以直接 lr,len=rll\to r,len=r-l 。当然如果是闭区间,只需要改成 lr+1l\to r+1 即可。

    4、上面的第二个方案,发现最终可能存在很多数轴上的点 ii 只与 i1,i+1i-1,i+1 连了 f=k,c=0f=k,c=0 的边,所以是没用的,离散化掉就好了。

    const int N = 200010 ;
    const int I = 998244353 ;
    
    #define ft first
    #define sc second
    #define to(k) e[k].to
    #define fr(k) e[k].from
    #define fw(k) e[k].flow
    #define ct(k) e[k].cost
    #define next(k) e[k].next
    #define pint pair<int, int>
    
    struct Edge{
        int to ;
        int from ;
        int flow ;
        int cost ;
        int next ;
    }e[N * 2] ;
    
    int _s ;
    int _t ;
    int ans ;
    int res ;
    int cnt ;
    int n, m ;
    int g[N] ;
    int vis[N] ;
    int dis[N] ;
    int pre[N] ;
    int head[N] ;
    int _last[N] ;
    queue <int> q ;
    
    void add(int u, int v, int f, int c){
        to(++ cnt) = v ;
        next(cnt) = head[u] ;
        fw(cnt) = f ; ct(cnt) = c ;
        fr(cnt) = u ; head[u] = cnt ;
    }
    bool spfa(){
        fill(g, g + n + 1, I) ;
        fill(dis, dis + n + 1, I) ;
        fill(vis, vis + n + 1, 0) ;
        q.push(_s) ; vis[_s] = 1 ; dis[_s] = 0 ;
        while (!q.empty()){
            int x = q.front() ;
            q.pop() ; vis[x] = 0 ;
            for (int k = head[x] ; ~k ; k = next(k))
                if (dis[to(k)] > dis[x] + ct(k) && fw(k)){
                    dis[to(k)] = dis[x] + ct(k) ;
                    g[to(k)] = min(fw(k), g[x]) ;
                    pre[to(k)] = x ; _last[to(k)] = k ;
                    if (!vis[to(k)]){
                        q.push(to(k)) ;
                        vis[to(k)] = 1 ;
                    }
                }
        }
        return (bool)(dis[_t] < I) ;
    }
    void ek(){
        while (spfa()){
            int x = _t ;
            res += g[_t] ;
            ans += g[_t] * dis[_t] ;
            //cout << g[_t] << " " << ans << endl ;
            while (x != _s){
                fw(_last[x]) -= g[_t] ;
                fw(_last[x] ^ 1) += g[_t] ;
                x = pre[x] ;
            }
        }
    }
    
    int len[N] ;
    pint base[N] ;
    int _n, _k, tot ;
    map <int, int> Id, buc ;
    map <int, int> :: iterator t ;
    
    int main(){
        int u, v, f, c ;
        cin >> _n >> _k ; cnt = -1 ;
        memset(head, -1, sizeof(head)) ;
        for (int i = 1 ; i <= _n ; ++ i){
            cin >> base[i].ft >> base[i].sc ;
            len[i] = base[i].sc - base[i].ft ;
        }
        for (int i = 1 ; i <= _n ; ++ i){
            if (!Id.count(base[i].ft)) buc[base[i].ft] ++ ;
            if (!Id.count(base[i].sc)) buc[base[i].sc] ++ ;
        }
        add(0, 1, _k, 0) ; add(1, 0, 0, 0) ;
        for (t = buc.begin() ; t != buc.end() ; ++ t)
            Id[t->ft] = ++ tot ; _s = 0 ; _t = tot + 1 ;
        for (int i = 1 ; i <= tot ; ++ i)
            add(i, i + 1, I, 0), add(i + 1, i, 0, 0) ;
        for (int i = 1 ; i <= _n ; ++ i){
            //cout << Id[base[i].ft] << " " << Id[base[i].sc] << endl ;
            add(Id[base[i].ft], Id[base[i].sc], 1, -len[i]) ;
            add(Id[base[i].sc], Id[base[i].ft], 0, len[i]) ;
        }
        n = _t + 1 ; ek() ;
        cout << -ans << endl ;
    }
    
    • 1

    信息

    ID
    2432
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者