• [六省联考2017]寿司餐厅 网络流


    题面

    [六省联考2017]寿司餐厅

    题解

    首先每种权值只取一次,且不同权值之间有类似于取了xx就必须取xx这种限制,因此我们可以判断这是一个最大权闭合子图问题。
    然后我们开始建图

    • 对于每个编号为(x),权值为(len)的区间,如果权值为正,则连s ---> x : len ;否则连x ---> t : - len;
    • 每个区间([l, r])([l + 1, r])([l, r - 1])(INF)边,表示取([l, r])就必须取这个区间内的所有区间。
    • 对于每个区间([i, i]),连向第(i)个寿司,权值为(INF).表示取了这个区间就必须取对应的寿司。
    • 对于每个寿司x,连x ---> t : id[x] ; 表示选第x个寿司,可以获得对应的大小为代号的代价。
    • 对于每个寿司x, 向对应的寿司代号连(INF)边,表示取了这个寿司就会取走相应的寿司类型。
    • 对于每个寿司类型,连x ---> t : m * id[x] * id[x]

    然后跑网络流即可。
    答案即为所有区间正权之和减去最大流。

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    #define R register int
    #define LL long long
    #define N 110
    #define AC 50000
    #define ac 820000
    #define inf 1000000000
    
    int n, m, addflow, x, s, t, all, cnt;
    LL ans;
    int Head[AC], date[ac], Next[ac], haveflow[ac], tot = 1;
    int last[AC], have[AC], c[AC], good[AC], may[AC];
    int q[AC], head, tail;
    int w[N][N], id[N][N], ID[AC];
    
    inline int read()
    {
        int x = 0;char c = getchar();bool z = false;
        while(c > '9' || c < '0') {if(c == '-') z = true; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        if(!z) return x;
        else return -x;
    }
    
    inline void upmin(int &a, int b) {if(b < a) a = b;}
    inline void upmax(int &a, int b) {if(b > a) a = b;}
    
    inline void add(int f, int w, int S)
    {
        date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S;
        date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = 0;
        //printf("%d ---> %d : %d
    ", f, w, S);
    }
    
    void pre()
    {
        n = read(), m = read(), all = n;//一开始给每个代号先建好点
        for(R i = 1; i <= n; i ++) ID[i] = may[i] = read();//代号,用于计算代价
        for(R i = 1; i <= n; i ++)
            for(R j = i; j <= n; j ++)
                w[i][j] = read(), id[i][j] = ++ all;//编号,用于网络流建图
        s = all + 1, t = s + 1;
    }
    
    int half(int x)
    {
        int l = 1, r = cnt, mid;
        while(l < r)
        {
            mid = (l + r) >> 1;
            if(may[mid] == x) return mid;
            else if(may[mid] < x) l = mid + 1;
            else r = mid - 1;
        }
        return l;
    }
    
    void build()
    {
    //	for(R i = 1; i <= n; i ++) add(i, t, m * i * i);
        sort(may + 1, may + n + 1);
        for(R i = 1; i <= n; i ++)
            if(may[i] != may[i + 1]) may[++ cnt] = may[i];
        for(R i = 1; i <= n; i ++) ID[i] = half(ID[i]);//找到这个代号对应的下标
        for(R i = 1; i <= cnt; i ++) add(i, t, m * may[i] * may[i]);//第i种代号的编号为i
        for(R i = 1; i <= n; i ++)
            for(R j = i; j <= n; j ++)
            {
                if(w[i][j] > 0) add(s, id[i][j], w[i][j]), ans += w[i][j];
                else add(id[i][j], t, - w[i][j]);
                if(i + 1 <= j) add(id[i][j], id[i + 1][j], inf);
                if(i <= j - 1) add(id[i][j], id[i][j - 1], inf);
                if(i == j) add(id[i][j], ID[i], inf), add(id[i][j], t, may[ID[i]]);//先向对应的代号连边,表示mx^2的代价...error !!!应该是付出ID[i]的代价,而不是i
            }//再向t连边,表示c * x的代价
        /*for(R i = 1; i <= n; i ++)
            for(R j = i; j <= n; j ++)
                printf("id[%d][%d] = %d
    ", i, j, id[i][j]);*/
    }
    
    void bfs()
    {
        q[++ tail] = t, have[1] = c[t] = 1;
        while(head < tail)
        {
            int x = q[++ head];
            for(R i = Head[x]; i; i = Next[i])
            {
                int now = date[i];
                if(!c[now] && haveflow[i ^ 1])
                    have[c[now] = c[x] + 1] ++, q[++ tail] = now;
            }
        }
        memcpy(good, Head, sizeof(Head));
    }
    
    void aru()
    {
        while(x != s)
        {
            haveflow[last[x]] -= addflow;
            haveflow[last[x] ^ 1] += addflow;
            x = date[last[x] ^ 1];
        }
        ans -= addflow, addflow = inf;
    }
    
    void ISAP()
    {
        bool done = false;
        x = s, addflow = inf;
        while(c[t] != all + 10)
        {
            if(x == t) aru();
            done = false;
            for(R &i = good[x]; i; i = Next[i])
            {
                int now = date[i];
                if(c[now] == c[x] - 1 && haveflow[i])
                {
                    upmin(addflow, haveflow[i]);
                    last[now] = i, done = true;
                    x = now; break;
                }
            }
            if(!done)
            {
                int go = all + 9;
                for(R i = Head[x]; i; i = Next[i])
                    if(c[date[i]] && haveflow[i]) upmin(go, c[date[i]]);
            if(!(-- have[c[x]])) break;
            have[c[x] = go + 1] ++;
            good[x] = Head[x];
            if(x != s) x = date[last[x] ^ 1];			
            }
        }
        printf("%lld
    ", ans);
    }
    
    int main()
    {
    //	freopen("in.in", "r", stdin);
        pre();
        build();
        bfs();
        ISAP();
    //	fclose(stdin);
        return 0;
    }
    
  • 相关阅读:
    CodeForces 916A Jamie and Alarm Snooze (水题)
    BZOJ 2440 [中山市选2011]完全平方数 (二分 + 莫比乌斯函数)
    BZOJ 4407 于神之怒加强版 (莫比乌斯反演 + 分块)
    HDU 1695 GCD (莫比乌斯反演)
    如何分析解决Android ANR
    Android网络编程系列 一 TCP/IP协议族之链路层
    Android网络编程系列 一 TCP/IP协议族之网际层
    Android网络编程系列 一 TCP/IP协议族之传输层
    Android网络编程系列 一 TCP/IP协议族
    Android网络编程系列 一 JavaSecurity之JSSE(SSL/TLS)
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10474386.html
Copyright © 2020-2023  润新知