CF103E Buying Sets题解
最近博客里奇奇怪怪的东西越来越多了,是时候写一篇题解了
这是模拟赛的一道题,考试时只会打暴力QWQ,后来发现这道题的建图真的十分巧妙
首先,看到\(n<=300\)的数据范围,很像是网络流,我们先把子集和子集中的元素拆开来看,将子集的价值取相反数,最后输出答案时再取个相反数,于是便为选了这个子集就必须要选其中的元素,使得选出的子集价值尽量大,将子集向元素连\(inf\)边,那么这就是一个最大权闭合子图的模型,
然后难点在于如何处理使得子集并的大小等于子集个数,看到题目中给的“任意\(k\)个子集的并的大小\(k\ge k\)”,我们可以想一想如何从这个角度来建图,由源点向子集连\(lim+P_i\)的边(\(lim\)是比\(inf\)小的一个极大值,防止\(inf\)边被割掉),这条边被割意味着不选这个子集,由元素向汇点连\(lim\)边,这条边被割意味着这个元素最终会被选,这里可以感性理解一下画个图手玩一下,最终被割掉的边肯定为\(n\)条,并且任意\(k\)个子集的并的大小\(k\ge k\)
放个代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int s,t,dep[1003],n,t1,x,p,ans;
vector<int>l[1003],l1[1003],nu[1003];
queue<int>q;
void bfs()
{
memset(dep,0,sizeof(dep));
q.push(s),dep[s]=1;
while(!q.empty())
{
p=q.front(),q.pop();
for(int j=0;j<l[p].size();j++)
if(l1[p][j]!=0&&dep[l[p][j]]==0)
q.push(l[p][j]),dep[l[p][j]]=dep[p]+1;
}
}
int dfs(int x1,int wat)
{
if(x1==t)
return wat;
int sum=0,cnt;
for(int j=0;j<l[x1].size();j++)
if(l1[x1][j]!=0&&dep[l[x1][j]]==dep[x1]+1)
cnt=dfs(l[x1][j],min(l1[x1][j],wat)),l1[x1][j]-=cnt,l1[l[x1][j]][nu[x1][j]]+=cnt,sum+=cnt,wat-=cnt;
if(sum==0)
dep[x1]=0;
return sum;
}
void add(int x,int y,int wat)
{
l[x].push_back(y),l[y].push_back(x),l1[x].push_back(wat),l1[y].push_back(0),nu[x].push_back(l[y].size()-1),nu[y].push_back(l[x].size()-1);
}
signed main()
{
scanf("%lld",&n);
s=0,t=2*n+1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&t1);
add(i+n,t,5e8);
for(int j=1;j<=t1;j++)
{
scanf("%lld",&x);
add(i,x+n,2e9);
}
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
add(s,i,5e8-x),ans+=5e8-x;
}
while(1)
{
bfs();
if(dep[t]==0)
break;
ans-=dfs(s,2e9);
}
cout<<min(0ll,-ans);
return 0;
}