【最大权闭合子图】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\n",ans-dinic());
95     return 0;
96 }

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

END

原文地址:https://www.cnblogs.com/antiquality/p/10485231.html

时间: 2024-08-26 23:24:24

【最大权闭合子图】bzoj4873: [Shoi2017]寿司餐厅的相关文章

bzoj4873: [Shoi2017]寿司餐厅(最小割)

传送门 大佬们是怎么一眼看出这是一个最大权闭合子图的……大佬好强->这里 1.把所有区间$(i,j)$看成一个点,如果权值大于0,则从$S$向他连边,容量为权值,否则从它向$T$连边,容量为权值的相反数 2.对于区间$(i,j)$,向所有的寿司$i$到$j$连边,表示选这个区间这些寿司必须选 3.对每一个寿司类型$w[i]$,为他们各自开一个点,向$T$连边,容量为$m*w[i]*w[i]$ 4.对每一个寿司向他们所属的类型$w[i]$连边,容量为$inf$,向$T$连边,容量为$w[i]$ 5

bzoj4873 [Shoi2017]寿司餐厅

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4873 [题解] 没看出来是最大权闭合子图模型--要多学习学习qwq 首先区间$[i,j]$依赖于区间$[i+1,j]$和$[i,j-1]$.每个区间$[i,j](i < j)$的权值就是$d_{i,j}$. 特别地,区间$[i, i]$的权值为$d_{i,j} - a_i$(由于花费原因). 区间$[i, i]$还依赖于$a_i$这个点,$a_i$这个点的权值为$-m * a_i * a_

BZOJ 4873 [Shoi2017]寿司餐厅 | 网络流 最大权闭合子图

链接 BZOJ 4873 题解 当年的省选题--还记得蒟蒻的我Day1 20分滚粗-- 这道题是个最大权闭合子图的套路题.严重怀疑出题人就是先画好了图然后照着图编了个3000字的题面.和我喜欢的妹子当年给别人写的情书一样长-- 最大权闭合子图 最大权闭合子图问题:一个有向图中,每个点带有一个权值(有正有负),有向边\(u \to v\)表示选\(u\)必须选\(v\),选出一些点使权值和最大,问权值和最大是多少. 最大权闭合子图的解法:网络流建图,对于每个点\(u\),设权值为\(w_u\),如

BZOJ_4873_[Shoi2017]寿司餐厅_最大权闭合子图

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=4873 分析:我们发现分数正负都有,并且之间有依赖关系,很容易想到最大权闭合子图. 建图: 1.S向正点连边,负点向T连边. 2.选了[i~j]显然要选[i+1~j]和[i~j-1],分别连边. 3.对于i==j的点,向对应的寿司连边. 4.总花费m*x*x+c*x拆成两部分.对于每个代号x,向T连容量为m*x*x的边,c*x这部分我们考虑算f[i][i]时把f[i][i]的值减掉x,当然也可

BZOJ:4873: [Shoi2017]寿司餐厅

4873: [Shoi2017]寿司餐厅 首先很开心在膜你赛的时候做了出来. 看到数据范围,看到不能dp,看到贡献去重后计算,咦,流? 那就容易了,转最大权闭合子图,每个区间建一个点,取了就一定要取他的子区间(依赖关系),代价上也很容易用依赖关系搞. 提交完A了就没理,后来同学说我#1了??? #include<cstdio> #include<algorithm> #define MN 40001 using namespace std; int read_p,read_ca,r

我和最大权闭合子图

第一次接触最大权闭合子图大概是2017年3月27号星期一,那段时间有5个同学(ZJC/LKQ/LWD/WJJ/...)去了湖南师大附中听PTY.BK他们讲课,因为我没有去Hfu一直很怪罪.后来安排我和LXY在70去电子科大之前去考一周的试,上午LXY的Mom接送,下午我们坐metro到世纪城再让LXY的Mom送回学校上晚自习.那一天我去70,T1是分治Floyd,T2是轮廓线DP,T3是一道最大权闭合子图(后来知道是BZOJ 3774 最优选择).那时我只会Dinic的模板,我的初高中学长Ana

Bzoj4873 [SXOI2017]寿司餐厅

Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 64  Solved: 45 Description Kiana最近喜欢到一家非常美味的寿司餐厅用餐.每天晚上,这家餐厅都会按顺序提供n种寿司,第i种寿司有一个 代号ai和美味度di,i,不同种类的寿司有可能使用相同的代号.每种寿司的份数都是无限的,Kiana也可以无限次 取寿司来吃,但每种寿司每次只能取一份,且每次取走的寿司必须是按餐厅提供寿司的顺序连续的一段,即Kiana 可以一次取走第1,2种寿

【暖*墟】#网络流# 最大权闭合子图

[相关概念详解] 闭合图:有向图的一个点集,且这个点集的所有出边仍然指向该点集. 最大权闭合图:(每一个点有一个权值)在所有的合法闭合图中,点权之和最大的图. 处理问题:权值有正有负,重复选只算一次,选择有相互关联性 的问题. 首先有一个有向连通图(闭合图),每个点带有一个权值,例如: 造出一个超级源点S和一个超级汇点T,把S连边到所有带有正权的点上,每条边的容量是这个点的权: 把所有带负权的点连边到T,每条边的容量是这个点的权的相反数(正值).原来的边的容量设成无限大. 所有的点按权值的正负连

HDU5772 String problem(最大权闭合子图)

题目..说了很多东西 官方题解是这么说的: 首先将点分为3类 第一类:Pij 表示第i个点和第j个点组合的点,那么Pij的权值等于w[i][j]+w[j][i](表示得分) 第二类:原串中的n个点每个点拆出一个点,第i个点权值为 –a[s[i]] (表示要花费) 第三类:对于10种字符拆出10个点,每个点的权值为  -(b[x]-a[x]) 那么我们可以得到一个关系图 ,对于第一类中的点Pij,如果想要选择Pij,你就必须要选中第二类中的点i和j,对于第二类中的点如果你想选中第i个点,其对应的字