最大权闭合子图bzoj4873: [Shoi2017]寿司餐厅
Posted antiquality
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大权闭合子图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
以上是关于最大权闭合子图bzoj4873: [Shoi2017]寿司餐厅的主要内容,如果未能解决你的问题,请参考以下文章
最大权闭合子图bzoj4873: [Shoi2017]寿司餐厅