• 【最大权闭合子图】bzoj4873: [Shoi2017]寿司餐厅


    为什么跑得这么慢

    Description

    Kiana最近喜欢到一家非常美味的寿司餐厅用餐。每天晚上,这家餐厅都会按顺序提供n种寿司,第i种寿司有一个
    代号ai和美味度di,i,不同种类的寿司有可能使用相同的代号。每种寿司的份数都是无限的,Kiana也可以无限次
    取寿司来吃,但每种寿司每次只能取一份,且每次取走的寿司必须是按餐厅提供寿司的顺序连续的一段,即Kiana
    可以一次取走第1,2种寿司各一份,也可以一次取走第2,3种寿司各一份,但不可以一次取走第1,3种寿司。由于餐
    厅提供的寿司种类繁多,而不同种类的寿司之间相互会有影响:三文鱼寿司和鱿鱼寿司一起吃或许会很棒,但和水
    果寿司一起吃就可能会肚子痛。因此,Kiana定义了一个综合美味度di,j(i<j),表示在一次取的寿司中,如果包含
    了餐厅提供的从第i份到第j份的所有寿司,吃掉这次取的所有寿司后将获得的额外美味度。由于取寿司需要花费一
    些时间,所以我们认为分两次取来的寿司之间相互不会影响。注意在吃一次取的寿司时,不止一个综合美味度会被
    累加,比如若Kiana一次取走了第1,2,3种寿司各一份,除了d1,3以外,d1,2,d2,3也会被累加进总美味度中。神奇
    的是,Kiana的美食评判标准是有记忆性的,无论是单种寿司的美味度,还是多种寿司组合起来的综合美味度,在
    计入Kiana的总美味度时都只会被累加一次。比如,若Kiana某一次取走了第1,2种寿司各一份,另一次取走了第2,3
    种寿司各一份,那么这两次取寿司的总美味度为d1,1+d2,2+d3,3+d1,2+d2,3,其中d2,2只会计算一次。奇怪的是,
    这家寿司餐厅的收费标准很不同寻常。具体来说,如果Kiana一共吃过了c(c>0)种代号为x的寿司,则她需要为这些
    寿司付出mx^2+cx元钱,其中m是餐厅给出的一个常数。现在Kiana想知道,在这家餐厅吃寿司,自己能获得的总美
    味度(包括所有吃掉的单种寿司的美味度和所有被累加的综合美味度)减去花费的总钱数的最大值是多少。由于她
    不会算,所以希望由你告诉她

    Input

    第一行包含两个正整数n,m,分别表示这家餐厅提供的寿司总数和计算寿司价格中使用的常数。
    第二行包含n个正整数,其中第k个数ak表示第k份寿司的代号。
    接下来n行,第i行包含n-i+1个整数,其中第j个数di,i+j-1表示吃掉寿司能
    获得的相应的美味度,具体含义见问题描述。
    N<=100,Ai<=1000

    Output

    输出共一行包含一个正整数,表示Kiana能获得的总美味度减去花费的总钱数的最大值。


    题目分析

    以后这样一类的点权最大化问题可以向最大权闭合子图的方向考虑。

    对于区间$d_{i,j}$向$d_{i+1,j},d_{i,j-1}$连边;再对于每一种$a_i$建个点,从$d_{i,i}$向$a_i$连边。

    剩下的就是一样的模型了。

     1 #include<bits/stdc++.h>
     2 const int maxn = 103;
     3 const int maxm = 100035;
     4 const int maxNode = 50035;
     5 const int INF = 2e9;
     6 
     7 struct Edge
     8 {
     9     int u,v,f,c;
    10     Edge(int a=0, int b=0, int c=0, int d=0):u(a),v(b),f(c),c(d) {}
    11 }edges[maxm];
    12 int edgeTot,head[maxNode],nxt[maxm],lv[maxNode];
    13 int n,k,ans,S,T;
    14 int a[maxn],d[maxn][maxn],id[maxn][maxn],ida[1035],idt;
    15 
    16 int read()
    17 {
    18     char ch = getchar();
    19     int num = 0, fl = 1;
    20     for (; !isdigit(ch); ch=getchar())
    21         if (ch=='-') fl = -1;
    22     for (; isdigit(ch); ch=getchar())
    23         num = (num<<1)+(num<<3)+ch-48;
    24     return num*fl;
    25 }
    26 void addedge(int u, int v, int c)
    27 {
    28     edges[edgeTot] = Edge(u, v, 0, c), nxt[edgeTot] = head[u], head[u] = edgeTot, ++edgeTot;
    29     edges[edgeTot] = Edge(v, u, 0, 0), nxt[edgeTot] = head[v], head[v] = edgeTot, ++edgeTot;
    30 }
    31 bool buildLevel()
    32 {
    33     memset(lv, 0, sizeof lv);
    34     std::queue<int> q;
    35     lv[S] = 1, q.push(S);
    36     for (int tmp; q.size(); )
    37     {
    38         tmp = q.front(), q.pop();
    39         for (int i=head[tmp]; i!=-1; i=nxt[i])
    40         {
    41             int v = edges[i].v;
    42             if (!lv[v]&&edges[i].f < edges[i].c){
    43                 lv[v] = lv[tmp]+1, q.push(v);
    44                 if (v==T) return true;
    45             }
    46         }
    47     }
    48     return false;
    49 }
    50 int fndPath(int x, int lim)
    51 {
    52     int sum = 0;
    53     if (x==T||!lim) return lim;
    54     for (int i=head[x]; i!=-1&&sum < lim; i=nxt[i])
    55     {
    56         int v = edges[i].v, val;
    57         if (lv[v]==lv[x]+1&&edges[i].f < edges[i].c){
    58             if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f))))
    59                 edges[i].f += val, edges[i^1].f -= val, sum += val;
    60             else lv[v] = -1;
    61         }
    62     }
    63     return sum;
    64 }
    65 int dinic()
    66 {
    67     int ret = 0, val;
    68     while (buildLevel())
    69         if ((val = fndPath(S, INF))) ret += val;
    70     return ret;
    71 }
    72 int main()
    73 {
    74     memset(head, -1, sizeof head);
    75     n = read(), k = read(), S = 0, T = idt = 1;
    76     for (int i=1; i<=n; i++) a[i] = read();
    77     for (int i=1; i<=n; i++)
    78         for (int j=i; j<=n; j++)
    79         {
    80             d[i][j] = read(), id[i][j] = ++idt;
    81             if (d[i][j] > 0) addedge(S, id[i][j], d[i][j]), ans += d[i][j];
    82             else addedge(id[i][j], T, -d[i][j]);
    83         }
    84     for (int i=1; i<=n; i++)
    85     {
    86         addedge(id[i][i], T, a[i]);
    87         if (!ida[a[i]]) ida[a[i]] = ++idt, addedge(idt, T, k*a[i]*a[i]);
    88         addedge(id[i][i], ida[a[i]], INF);
    89     }
    90     for (int i=1; i<=n; i++)
    91         for (int j=i+1; j<=n; j++)
    92             addedge(id[i][j], id[i][j-1], INF), 
    93             addedge(id[i][j], id[i+1][j], INF);
    94     printf("%d
    ",ans-dinic());
    95     return 0;
    96 }

    以上两个标红的地方一改,从$848ms$->$106ms$.

    END

  • 相关阅读:
    获取窗口相对位置小工具
    关于抽奖概率的问题
    乔布斯的成功秘方:坚持思考两个问题
    清理svn生成的相关文件的小工具
    photoshop cs6 简体中文正式版下载
    .NET下office操作利器NPOI
    sql 取各组中的最大值
    c# winform 获取当前程序运行根目录
    C# Winform DataGridView使用总结 转
    c#安装数据库并自动修改Web.config类
  • 原文地址:https://www.cnblogs.com/antiquality/p/10485231.html
Copyright © 2020-2023  润新知