[ABC214H] Collecting(Primal-Dual 原始对偶算法)

题意

分析

前置知识:Primal-Dual 原始对偶算法

或许还有不需要前置知识的做法?但是我不会

首先考虑 Tarjan 缩点,然后转化成一个 DAG。

每个点只能被收集一次,故考虑网络流,套路性地将 i 拆点成 ini,outi,设源点为 s,t

建边(以下令 uv(w,c) 表示连接一条从 uv 的边,费用 c,):

  • sin1(k,0),表示 k 个人从 1 出发。
  • iniouti(1,ai),表示这个点的权值只能取一次。
  • iniouti(k,0),表示这个点可以被经过多次。
  • outuinv(k,0),表示这条边可以被经过多次。
  • outit(k,0),表示任何点都可以成为终点。

将原边权取反跑最小费用流。时间复杂度 O(knm),无法通过。

最短路算法复杂度已经足够大了,我们需要使用 Primal-Dual 算法。但是原图的边权是负数,初始化也需要 O(nm) 的复杂度,我们需要将初始边权转化为正数才能顺利使用 Primal-Dual。

暴力建图的思想是考虑流一条边加的贡献,由于原图是个 DAG,因此如果走了一条边,那么一定会有一些点之后不会再被遍历到,这些点实际上是拓扑序上位于该边起点和终点之间的点。

考虑每流一条边会失去哪些贡献,建图(用 dfni 表示拓扑序为 i 的点,rki 表表示 i 的拓扑序):

  • sin1(k,0j=1rk11adfnj),表示拓扑序在 1 之前的点不能被取。
  • iniouti(1,0),表示第一次到这个点不会失去贡献。
  • iniouti(k,ai),表示多次到这个点不能再取一遍,会失去 ai 的贡献。
  • outuinv(k,j=rku+1rkv1adfnj),表示走这条边拓扑序上位于起点终点之间的点不会再被走。
  • outit(k,j=rki+1nadfnj),表示以这个点结束那么拓扑序在其之后的所有点不能被取。

时间复杂度 O(kmlogm),可以通过。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<bitset>
#include<set>
#include<ctime>
#include<random>
#include<cassert>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0);
#define OTIE cout.tie(0);
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define popc __builtin_popcount
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define ALL(x) x.begin(),x.end()
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) (x&-x)
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define mem(x,y) memset(x,y,sizeof x)
//#define double long double
#define int long long
//#define int __int128
using namespace std;
typedef long long i64;
using pii=pair<int,int>;
bool greating(int x,int y){return x>y;}
bool greatingll(long long x,long long y){return x>y;}
inline int rd(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}return x*f;
}
inline void write(int x,char ch='\0'){
	if(x<0){x=-x;putchar('-');}
	int y=0;char z[40];
	while(x||!y){z[y++]=x%10+48;x/=10;}
	while(y--)putchar(z[y]);if(ch!='\0')putchar(ch);
}
bool Mbg;
const int maxn=4e5+5,maxm=2e6+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,m,k,b[maxn],c[maxn],s[maxn];
int u[maxn],v[maxn];
vector<int>G[maxn];
int dfn[maxn],low[maxn],dfncnt;
int sta[maxn],tp;
int scc[maxn],sc;
bool vis[maxn];
void tar(int x){
	dfn[x]=low[x]=++dfncnt,vis[x]=1,sta[++tp]=x;
	for(int u:G[x]){
		if(!dfn[u])tar(u),low[x]=min(low[x],low[u]);
		else if(vis[u])low[x]=min(low[x],dfn[u]);
	}
	if(low[x]==dfn[x]){
		int y;++sc;
		do{y=sta[tp--],scc[y]=sc,vis[y]=0;}while(x^y);
	}
}

int S,T;
struct edge{
	int to,nxt,w,c;
}a[maxm];
int head[maxn],edges;
void add(int x,int y,int z,int w){
	a[++edges]=(edge){y,head[x],z,w};
	head[x]=edges;
}
void add_edge(int x,int y,int z,int w){
	add(x,y,z,w),add(y,x,0,-w);
}
int dis[maxn],dinic[maxn];
int h[maxn];
int pre[maxn],e[maxn];
bool dij(){
	rep(i,1,T)dis[i]=dinic[i]=llinf,vis[i]=0;
	priority_queue<pii,vector<pii>,greater<pii> >q;
	dis[S]=0,q.push(mp(0,S));
	while(!q.empty()){
		pii nw=q.top();q.pop();
		if(vis[nw.se])continue;vis[nw.se]=1;
		graph(i,nw.se,head,a){
			int u=a[i].to,exdis=a[i].c+h[nw.se]-h[u];
			assert(!a[i].w||exdis>=0);
			if(a[i].w&&dis[u]>nw.fi+exdis){
				dis[u]=nw.fi+exdis,dinic[u]=min(dinic[nw.se],a[i].w),pre[u]=nw.se,e[u]=i;
				q.push(mp(dis[u],u));
			}
		}
	}
	return vis[T];
}
int EK(){
	int res=0,flow=0;
	while(dij()){
		flow+=dinic[T];
		assert(h[S]==0);
		res+=dinic[T]*(dis[T]+h[T]);
		rep(i,1,T)h[i]+=dis[i];
		int p=T;
		while(p^S){
			a[e[p]].w-=dinic[T],a[e[p]^1].w+=dinic[T];
			p=pre[p];
		}
	}
	return res;
}

void solve_the_problem(){
	n=rd(),m=rd(),k=rd();
	rep(i,1,m){
		int x=rd(),y=rd();
		u[i]=x,v[i]=y,G[x].emplace_back(y);
	}
	rep(i,1,n)b[i]=rd();
	
	rep(i,1,n)if(!dfn[i])tar(i);
//	rep(i,1,n)write(scc[i],32);P__;
	rep(i,1,n)G[i].clear();
	rep(i,1,n)c[scc[i]]+=b[i];
	rep(i,1,m){
		if(scc[u[i]]!=scc[v[i]]){
			assert(scc[u[i]]>scc[v[i]]);
			G[scc[u[i]]].emplace_back(scc[v[i]]);
		}
	}
	per(i,sc,1)s[i]=s[i+1]+c[i];
	
	S=2*sc+1,T=2*sc+2,edges=1;
	add_edge(S,scc[1],k,s[scc[1]+1]);
	rep(i,1,sc)add_edge(i,i+sc,1,0),add_edge(i,i+sc,k,c[i]),add_edge(i+sc,T,k,s[1]-s[i]);
	rep(i,1,sc){
		for(int u:G[i]){
			add_edge(i+sc,u,k,s[u+1]-s[i]);
		}
	}
	write(k*s[1]-EK());
}
bool Med;
signed main(){
//	freopen(".in","r",stdin);freopen(".out","w",stdout);
//	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=1;
	while(_--)solve_the_problem();
}
/*
5 5 10
1 2
2 3
3 2
1 4
1 5
1 4 5 2 8
*/

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18319078

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示