专栏文章
CF103E Buying Sets 题解
CF103E题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miobx96f
- 此快照首次捕获于
- 2025/12/02 16:41 3 个月前
- 此快照最后确认于
- 2025/12/02 16:41 3 个月前
我们把权值取反然后求最大权闭合子图,
题目给了条件,任意 个集合的并集,包含的不同数字不会少于 个。
即对于任意左部点集合 , ,我们给每个数字赋上 的权值,给每个集合赋上 的权值,发现选择的 的数量一定 选择的 的数量,所以当我们跑最大流的时候一定会选择数量相等的情况(这样流量最大)。
因为求的是最大权闭合子图,所以把源点 连向集合(正权值的点),容量为 ,数字(负权值的点)连向汇点 ,容量为 ,集合连向集合内的数字,容量 。
最后答案和 取 即可(即选择空集)。
附上代码:
C#include<bits/stdc++.h>
#define FL(i,a,b) for(ll i=(a);i<=(b);i++)
#define FR(i,a,b) for(ll i=(a);i>=(b);i--)
#define ll long long
using namespace std;
const ll MAXN = 6e2 + 10;
const ll MR = 1e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll lim = 1e18;
ll n,sum,S,T;
struct edge{
ll v,w,nxt;
}e[MR<<1];
ll head[MAXN],now[MAXN],cnt=1;
ll dis[MAXN];
void Add_edge(ll u,ll v,ll w){
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool bfs(){
queue<ll>q;
FL(i,0,(n<<1)+1) dis[i]=inf;
dis[S]=0,now[S]=head[S];
q.push(S);
while(!q.empty()){
ll u=q.front();
q.pop();
for(ll i=head[u];i;i=e[i].nxt){
ll v=e[i].v;
if(e[i].w&&dis[v]==inf){
dis[v]=dis[u]+1;
now[v]=head[v];
q.push(v);
if(v==T) return 1;
}
}
}
return 0;
}
ll dfs(ll u,ll flow){
if(u==T) return flow;
ll res=0;
for(ll i=now[u];i&&flow;i=e[i].nxt){
ll v=e[i].v;
now[u]=i;
if(e[i].w&&(dis[v]==dis[u]+1)){
ll tmp=dfs(v,min(flow,e[i].w));
if(!tmp) dis[v]=inf;
e[i].w-=tmp;
e[i^1].w+=tmp;
res+=tmp,flow-=tmp;
}
}
return res;
}
ll dinic(){
while(bfs()) sum-=dfs(S,inf);
return min(-sum,0ll);
}
signed main(){
scanf("%lld",&n),S=0,T=(n<<1)+1;
FL(i,1,n){
ll x,y;
scanf("%lld",&x);
FL(j,1,x){
scanf("%lld",&y);
Add_edge(i,y+n,inf),Add_edge(y+n,i,0);
}
}
FL(i,1,n){
ll x;
scanf("%lld",&x),sum+=lim-x;
Add_edge(S,i,lim-x),Add_edge(i,S,0);
}
FL(i,1,n) Add_edge(i+n,T,lim),Add_edge(T,i+n,0);
printf("%lld\n",dinic());
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...