Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

[HZNOI #koishi] Magic

[HZNOI #514] Magic

题意

给定一个 n 个点 m 条边的有向图, 每个点有两个权值 aibi, 可以以 bi 的花费把第 i 个点的 ai 变成 0. 最后每个点 i 产生的花费为所有从 i 出发能通过一条有向边直接到达的点 jajmax. 最小化这个过程中的总花费.

n\le 1000,m\le50000

题解

一点都不套路的最小割.

果然我是不会网络流的.

对于每个点, 如果将它的邻接点按照 a_j 降序排序的话, 不难发现必然要干掉一个前缀的所有 a_j 才能让这个点在最后统计的时候产生的花费变小. 但是多次干掉同一个点不能重复计算花费.

那么我们一点都不自然地想到最小割. 先把所有点拆成两个, 一个负责计算最终统计时的花费 (A类点), 一个负责计算被干掉的时候产生的花费 (B类点). 被干掉的时候产生的花费直接连一条流量为 b_i 的边到 t 就可以了. 最终统计时的花费先从 s 连一条 \infty 边到当前点, 然后按照 a_j 降序拉出一条链来, 链上的每个点代表一条边, 权值为这条边到达的点的 a_j. 然后再从链上的每个点连一条 \infty 边到 j 对应的点. 这样的话如果 s\verb|-|t 被割断, 那么对于每一个 A 类点, 后面必然是割掉了某个 a_j, 同时所有大于被割断的 a_j 的边邻接的点必然都已经被割掉了 b_i.

建图Dinic就可以了.

这个拉链然后最小割的套路依然没有学会...果然我还是太菜了QAQ...

什么你问我 n+m 个点Dinic怎么跑过去的? 我怎么知道?Dinic的运行速度大概都是靠信仰吧...

恋恋世界第一!

参考代码

#include <bits/stdc++.h>

const int MAXV=1e5+10;
const int MAXE=5e6+10;
const int INF=0x7FFFFFFF;

struct Edge{
	int from;
	int to;
	int flow;
	Edge* rev;
	Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* cur[MAXV];
Edge* top=E;

int v;
int e;
int a[1010];
int b[1010];
int depth[MAXV];
std::vector<int> link[1010];

bool BFS(int,int);
int Dinic(int,int);
int DFS(int,int,int);
void Insert(int,int,int);

int main(){
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	scanf("%d%d",&v,&e);
	for(int i=1;i<=v;i++)
		scanf("%d",a+i);
	for(int i=1;i<=v;i++)
		scanf("%d",b+i);
	for(int i=0;i<e;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		link[a].push_back(b);
	}
	for(int i=1;i<=v;i++)
		std::sort(link[i].begin(),link[i].end(),[](int a,int b){return ::a[a]>::a[b];});
	int s=0,t=1,cnt=v*2+1;
	for(int i=1;i<=v;i++){
		Insert(s,i+1,INF);
		Insert(i+v+1,t,b[i]);
		int last=i+1;
		for(size_t j=0;j<link[i].size();j++){
			++cnt;
			Insert(cnt,v+link[i][j]+1,INF);
			Insert(last,cnt,a[link[i][j]]);
			last=cnt;
		}
	}
	printf("%d\n",Dinic(s,t));
	return 0;
}

int Dinic(int s,int t){
	int ans=0;
	while(BFS(s,t))
		ans+=DFS(s,INF,t);
	return ans;
}

bool BFS(int s,int t){
	memset(depth,0,sizeof(depth));
	std::queue<int> q;
	q.push(s);
	depth[s]=1;
	cur[s]=head[s];
	while(!q.empty()){
		s=q.front();
		q.pop();
		for(Edge* i=head[s];i!=NULL;i=i->next){
			if(i->flow>0&&depth[i->to]==0){
				depth[i->to]=depth[s]+1;
				cur[i->to]=head[i->to];
				if(i->to==t)
					return true;
				q.push(i->to);
			}
		}
	}
	return false;
}

int DFS(int s,int flow,int t){
	if(s==t||flow<=0)
		return flow;
	int rest=flow;
	for(Edge*& i=cur[s];i!=NULL;i=i->next){
		if(i->flow>0&&depth[i->to]==depth[s]+1){
			int tmp=DFS(i->to,std::min(rest,i->flow),t);
			if(tmp<=0)
				depth[i->to]=0;
			rest-=tmp;
			i->flow-=tmp;
			i->rev->flow+=tmp;
			if(rest<=0)
				break;
		}
	}
	return flow-rest;
}

inline void Insert(int from,int to,int flow){
	top->from=from;
	top->to=to;
	top->flow=flow;
	top->rev=top+1;
	top->next=head[from];
	head[from]=top++;
	
	top->from=to;
	top->to=from;
	top->flow=0;
	top->rev=top-1;
	top->next=head[to];
	head[to]=top++;
}

posted @   rvalue  阅读(276)  评论(0编辑  收藏  举报
编辑推荐:
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
· C# 深度学习框架 TorchSharp 原生训练模型和图像识别
阅读排行:
· 这或许是全网最全的 DeepSeek 使用指南,95% 的人都不知道的使用技巧(建议收藏)
· 拒绝繁忙!免费使用 deepseek-r1:671B 参数满血模型
· 本地搭建DeepSeek和知识库 Dify做智能体Agent(推荐)
· Sdcb Chats 重磅更新:深度集成 DeepSeek-R1,思维链让 AI 更透明!
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
本博客已经弃用, 是否跳转到新博客的该文章?
点击右上角即可分享
微信分享提示