最大权闭合子图
前置知识:最大权闭合子图。
这是个什么东东呢,它是对于每一个点赋一个值,求一个点集,点集内的所有点都必须包含它的所有后继,使这个点集的和最大。
如以下图:
图中的编号代表点权。
可以知道的是,能选择的点集有:
最大的明显为
这种类型的题我们如何解决呢,首先对于点权为负的,我们对其于汇点建边,而为正的点则用源点与其建边,其他边不变,原来图上的边设为正无穷,而新建的边为点权的绝对值。
没有设边权的代表边权无限大。
那么我们在这上面跑一次最小割,然后用所有从源点出发的边权和减去最小割即可,如图中,边权和为
考虑证明:
首先割掉一条源点上的边代表你不会选择这个点,而割掉连向汇点上的一条边代表你会选择这个点。
很好说明,因为在割去汇点上的边时,代表你要选这个点,且正点也用流量与负点进行了抵消。
如果图不联通,那么选的点集一定合法,因为如果联通,说明你选择了正点,而正点有负点后继没有进行选择。
所以综上所言,我们就可以用最小割求出这个问题了。
那么如何证明这是最优解呢。
我们知道:最小割为:
而答案为:
那么这个最大权闭合子图有什么用呢,那就先看到我们这道题:CF311E Biologist。
这题一下貌似没什么思路,先进行分析。
首先我们假设所有点初始为
现在我们想满足一些
对于本来是
而对于本来是
那么可以把加操作看成点权为正,源点与其相连,而减操作看成点权为负,与汇点相连。
那么这次的最后答案是什么呢。
首先最小割为:
而由于我们假使所有数为
两者加起来即为所有询问的价值,即
最终答案即为
总结一下总过程:
1、对原来为
2、对原来为
3、对于每一个
4、对于每一个
建了边后跑最小割即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e6+5;
const int M=2e6+5;
int n,m,g,h[M],p[N],s,t,st[N],cnt,vis[N],dep[N];
struct node
{
int to,data,next;
}a[M];
inline void addedge(int x,int y,int k)
{
a[cnt]={y,k,h[x]};
h[x]=cnt++;
a[cnt]={x,0,h[y]};
h[y]=cnt++;
}
queue<int>q;
bool bfs()
{
//cout<<'b';
for(int i=1;i<=n+m+2;i++)dep[i]=0;
q.push(s);
dep[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=h[x];i!=-1;i=a[i].next)
{
if(a[i].data==0)continue;
if(!dep[a[i].to])
{
dep[a[i].to]=dep[x]+1;
q.push(a[i].to);
}
}
}
return dep[t];
}
int dfs(int x=s,int num=1e9)
{
if(x==t)return num;
if(vis[x])return 0;
for(int i=h[x];i!=-1;i=a[i].next)
{
if(a[i].data==0)continue;
if(dep[a[i].to]==dep[x]+1)
{
vis[x]=1;
int sum=dfs(a[i].to,min(num,a[i].data));
if(sum)
{
a[i].data-=sum;
a[i^1].data+=sum;
return sum;
}
}
}
return 0;
}
int main()
{
memset(h,-1,sizeof(h));
scanf("%d%d%d",&n,&m,&g);
s=n+m+1;
t=n+m+2;
for(int i=1;i<=n;i++)scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(p[i]==0)addedge(s,i,x);
else addedge(i,t,x);
}
int ans=0;
for(int i=1;i<=m;i++)
{
int opt,w,k,ss;
scanf("%d%d%d",&opt,&w,&k);
ans+=w;
for(int j=1;j<=k;j++)scanf("%d",&st[j]);
scanf("%d",&ss);
for(int j=1;j<=k;j++)
{
if(opt==0)addedge(i+n,st[j],w+ss*g);
else addedge(st[j],i+n,w+ss*g);
}
if(opt==0)addedge(s,i+n,w+ss*g);
else addedge(i+n,t,w+ss*g);
}
while(bfs())
{
memset(vis,0,sizeof(vis));
int num=dfs();
while(num)
{
ans-=num;
memset(vis,0,sizeof(vis));
num=dfs();
}
}
printf("%d",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话