又是经典模型的好题目

题目描述

人类智慧之神 zhangzj 最近有点胖,所以要减肥,他买了 NN 种减肥药,发现每种减肥药使用了若干种药材,总共正好有 NN 种不同的药材。

经过他的人脑实验,他发现如果他吃下去了 KK(0≤K≤N0≤K≤N)种减肥药,而这 KK 种减肥药使用的药材并集大小也为 KK,这 KK 种才会有效果,否则无效。
第 ii 种减肥药在产生效果的时候会使 zhangzj 的体重增加 PiPi​ 斤,显然 PiPi​ 可以小于 00。

他想知道,一次吃药最好情况下体重变化量是多少,当然可以一种药也不吃,此时体重不变。 由于某些奥妙重重的情况,我们可以让这 NN 种减肥药每一种对应一个其使用的药材,且 NN 种减肥药对应的药材互不相同(即有完美匹配)。

输入格式

第一行一个整数 NN。

接下来 NN 行,每行描述一种减肥药,对于一种减肥药,第一个数读入使用的药材个数 tt,接下来 tt 个整数表示使用的药材编号,一个药材编号在一行只会出现一次。
最后一行 NN 个整数,第 ii 个整数 PiPi​ 表示第 ii 种减肥药产生效果时的体重变化量。

数据范围与提示

对于 30%30% 的数据,N≤20N≤20;
对于另外 10%10% 的数据,Pi<0Pi​<0;
对于 100%100% 的数据,1≤N≤300,∣Pi∣≤10000001≤N≤300,∣Pi​∣≤1000000。


题目分析

这道题最主要的麻烦在于药材和药的数目要相同,这意味着最优情况下,可能需要选取一些收益为负的药来达到合法状态。因此wqs二分药材权值负增量的做法就会挂得很彻底。

再观察题目的特殊性质,发现给出的一张图是恰好两侧点数相同,且具有完美匹配的二分图。这个条件说明在左侧任意选取$k$个点,右侧对应的$k'$必定有$k \le k'$。这是一个相当重要的条件。在这个前提下,我们可以把每种减肥药的权值改为$BASE-P_i$;药材的权值设成$BASE$,再以此做最大权闭合子图。关于这个做法的解释如下所示:

这里是第一个例子,最优方案是三个全选,收益为20。考虑一下“表面权值和最优”(但是不合法)的选前两个的方案,由于我们在右侧每个药材都连出了$BASE$的边,那么当左边选取$k$个、右边选取$k\le k'$个时,进行的操作相当于是选了右侧$k'\times BASE$的割,然而很明显选左侧$k\times BASE-\sum P_i$的割更优。但在最大权闭合子图的方面看,割去$(S,i)$也即意味着点$i$就是不选了,这与前一假设矛盾。因此,右边边权的$BASE$增量能够致使$k\ge k'$,那么综上所述保证了$k=k'$。

以下是第二个例子。在这个例子中,为了保证合法,我们不得不选取增肥100的减肥药。同样是考虑选取前两个元素的方案,会发现仍然由于右侧有三个$BASE,所以导致只有在加入第三个元素时才得到最大值。

 #include<bits/stdc++.h>
const int maxn = ;
const int maxm = ;
const int INF = 2e9;
const int base = 5e6; struct Edge
{
int u,v,f,c;
Edge(int a=, int b=, int c=, int d=):u(a),v(b),f(c),c(d) {}
}edges[maxm];
int edgeTot,head[maxn],nxt[maxm],lv[maxn];
int n,ans,tot,S,T; void addedge(int u, int v, int c)
{
edges[edgeTot] = Edge(u, v, , c), nxt[edgeTot] = head[u], head[u] = edgeTot, ++edgeTot;
edges[edgeTot] = Edge(v, u, , ), nxt[edgeTot] = head[v], head[v] = edgeTot, ++edgeTot;
}
bool buildLevel()
{
std::queue<int> q;
memset(lv, , sizeof lv);
q.push(S), lv[S] = ;
for (int tmp; q.size(); )
{
tmp = q.front(), q.pop();
for (int i=head[tmp]; i!=-; i=nxt[i])
{
int v = edges[i].v;
if (!lv[v]&&edges[i].f < edges[i].c){
lv[v] = lv[tmp]+, q.push(v);
if (v==T) return true;
}
}
}
return false;
}
int fndPath(int x, int lim)
{
int sum = ;
if (x==T||!lim) return lim;
for (int i=head[x]; i!=-&&sum < lim; i=nxt[i])
{
int v = edges[i].v, val;
if (lv[v]==lv[x]+&&edges[i].f < edges[i].c){
if ((val = fndPath(v, std::min(lim-sum, edges[i].c-edges[i].f)))){
edges[i].f += val, edges[i^].f -= val, sum += val;
}else lv[v] = -;
}
}
return sum;
}
int dinic()
{
int ret = , val;
while ((buildLevel()))
while ((val = fndPath(S, INF))) ret += val;
return ret;
}
int main()
{
freopen("loj6045.in","r",stdin);
freopen("loj6045.out","w",stdout);
memset(head, -, sizeof head);
scanf("%d",&n), S = , T = *n+;
for (int i=,k; i<=n; i++)
{
scanf("%d",&k);
for (int u; k; --k)
scanf("%d",&u), addedge(i, u+n, INF);
}
for (int i=,s; i<=n; i++)
{
scanf("%d",&s);
addedge(S, i, base-s);
addedge(i+n, T, base);
tot += base-s;
}
printf("%d\n",-tot+dinic());
return ;
}

END

【思维题 最大权闭合子图】loj#6045. 「雅礼集训 2017 Day8」价的更多相关文章

  1. LOJ#6045. 「雅礼集训 2017 Day8」价(最小割)

    题面 传送门 题解 首先先把所有权值取个相反数来求最大收益,因为最小收益很奇怪 然后建图如下:\(S\to\)药,容量\(\inf+p_i\),药\(\to\)药材,容量\(\inf\),药材\(\t ...

  2. 【LYOI 212】「雅礼集训 2017 Day8」价(二分匹配+最大权闭合子图)

    「雅礼集训 2017 Day8」价 内存限制: 512 MiB时间限制: 1000 ms 输入文件: z.in输出文件: z.out   [分析] 蛤?一开始看错题了,但是也没有改,因为不会做. 一开 ...

  3. loj #6046. 「雅礼集训 2017 Day8」爷

    #6046. 「雅礼集训 2017 Day8」爷 题目描述 如果你对山口丁和 G&P 没有兴趣,可以无视题目背景,因为你估计看不懂 …… 在第 63 回战车道全国高中生大赛中,军神西住美穗带领 ...

  4. LOJ#6046. 「雅礼集训 2017 Day8」爷(分块)

    题面 传送门 题解 转化为\(dfs\)序之后就变成一个区间加,区间查询\(k\)小值的问题了,这显然只能分块了 然而我们分块之后需要在块内排序,然后二分\(k\)小值并在块内二分小于它的元素--一个 ...

  5. LOJ#6044. 「雅礼集训 2017 Day8」共(Prufer序列)

    题面 传送门 题解 答案就是\(S(n-k,k)\times {n-1\choose k-1}\) 其中\(S(n,m)\)表示左边\(n\)个点,右边\(m\)个点的完全二分图的生成树个数,它的值为 ...

  6. LOJ #6044 -「雅礼集训 2017 Day8」共(矩阵树定理+手推行列式)

    题面传送门 一道代码让你觉得它是道给初学者做的题,然鹅我竟没想到? 首先考虑做一步转化,我们考虑将整棵树按深度奇偶性转化为一张二分图,即将深度为奇数的点视作二分图的左部,深度为偶数的点视作二分图的右部 ...

  7. [LOJ#6044]. 「雅礼集训 2017 Day8」共[二分图、prufer序列]

    题意 题目链接 分析 钦定 \(k\) 个点作为深度为奇数的点,有 \(\binom{n-1}{k-1}\) 种方案. 将树黑白染色,这张完全二分图的生成树的个数就是我们钦定 \(k\) 个点之后合法 ...

  8. [LOJ 6031]「雅礼集训 2017 Day1」字符串

    [LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...

  9. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

随机推荐

  1. 6、python数据类型之元组(dict)

    字典字典的每个元素就是一个键值对,格式如下key:value{key1:value1,key2:value2,......} 1.创建 dict_eg = { "name":&qu ...

  2. P3823_[NOI2017]蚯蚓排队 哈希+脑子

    之前就写过一遍,今天却写挂了,查了半天发现是数组名写错啦$qwq$ 观察到$K$很小,所以使得我们可以哈希(怎么什么都能哈希$qwq$).我们把长度小于等于$50$的子串扔到哈希表里,并统计出现次数, ...

  3. 树的计数 Prufer序列+Cayley公式

    先安利一发.让我秒懂.. 第一次讲这个是在寒假...然而当时秦神太巨了导致我这个蒟蒻自闭+颓废...早就忘了这个东西了... 结果今天老师留的题中有两道这种的:Luogu P4981 P4430 然后 ...

  4. Silverlight 登陆界面

    美术水平有限,不喜勿喷. 界面代码,效果如下图 <UserControl x:Class="ElecDemoTelerikSL.Login" xmlns="http ...

  5. LeetCode 260 Single Number III 数组中除了两个数外,其他的数都出现了两次,找出这两个只出现一次的数

    Given an array of numbers nums, in which exactly two elements appear only once and all the other ele ...

  6. JSONP 回调给全局变量赋值失败解决

    ;//回调结束标志位var 临时全局变量;var 需要接收的全局变量: function getDate(){ flag = 0; //回调 inviteService.getActivityDeta ...

  7. <llinux下kvm虚拟化>

    原理就是本来可能要10台物理机完成的事现在只要5台,分别在每台物理机上虚拟一台,这5太虚拟机共享一个stronge,比如有一台物理机down掉后或是要做维护,我们可以把它上面的虚拟机牵走,从而减少损失 ...

  8. usb-host一步一步学(二)安卓在usb-host模式下列出当前连接的usb设备

    之前写了一个简单的例子usb-host一步一步学(一)安卓在usb-host模式下列出当前连接的usb设备,下面的这个例子是获取各种usb设备.usb接口以及usb连接点(endpoint) 正如上一 ...

  9. 斐波那契数列【java实现】

    java 实现斐波那契数列 以下是Java代码实现(递归与递推两种方式): import java.util.Scanner; /** * Fibonacci * * @author tongqian ...

  10. Oracle Hyperion Planning 11.1 .1:创建与管理应用程序 第1课:Planning概述

    第1课:Planning概述 1.说明 Oracle Enterprise Performance Management system Oracle Enterprise Performance Ma ...