【XSY3396】地铁(平面图与对偶图,最小割)

题意:给一张平面图,满足这张平面图的对偶图是一棵树,有若干限制,形如 “若经过点 \(x\) 则必须要经过点 \(y\)”,求 \(1\sim n\) 的最短路。

由于平面图与其对偶图互为对偶,所以平面图最短路等于其对偶图上的最小割。

在这里插入图片描述

注意这里的最小割和普通的最小割有点不一样,这里要求任意一条 \(S\to T\) 的路径上都只有一条割边(不然就不能对应平面图上的一条最短路),于是我们在求最小割时要给每条边加上容量为 INF 的反边。

例:

在这里插入图片描述

假设对偶图左图长这样(尽管这不是一棵树),那么我们的最小割算法会把 \(A\) 划分到 \(T\) 集,\(B\) 划分到 \(S\) 集,那么这样割掉的边为 \((S,A)\)\((B,T)\),此时得到最小割 \(2\),如中图。

但你会发现这样就有一条从 \(S\to T\) 的路径上有两条边被割掉了(如右图中蓝色路径),是不合法的。

所以我们需要对每一条边都连一条容量为 INF 的反边,以确保任意一条 \(S\to T\) 的路径上都只有一条割边(否则就必然会割掉某条 INF 反边,显然最小割不是这个)。

而对于题目的限制,我们先考虑 “平面图上经过了点 \(i\)” 在对偶图上对应着什么:

在这里插入图片描述

如图,设以 \(i\) 为右端点的最长的线段所对应的节点为 \(maxl_i\),以 \(i\) 为左端点的最长的线段所对应的节点为 \(maxr_i\),容易证明 \(maxl_i\)\(maxr_i\) 的父亲为同一个点 \(lca_i\)

那么 “平面图上经过了点 \(i\)” 等价于如图中蓝色的两条链上有任意一条边被割,发现这等价于 \(lca\) 被分到 \(S\) 集:因为蓝色的两条链上有任意一条边被割一定使得 \(lca\) 被分到 \(S\) 集,而 \(lca\) 被分到 \(S\) 集一定使得蓝色的两条链上某一条边被割(因为不可能割叶子到 \(T\) 的 INF 边)。

那么对于题目中的一条限制 \(a,b\),就等价于 \(lca_a\)\(lca_b\) 必须同在 \(S\) 集或同在 \(T\) 集。这很好处理,直接在 \(lca_a\)\(lca_b\) 之间连一条双向 INF 边即可。

建树有些细节,注意要特判哪些点是压根不可能经过的。

代码如下:

#include<bits/stdc++.h>

#define N 1010
#define INF 0x7fffffff

using namespace std;

inline int read()
{
	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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

struct Edge
{
	int u,v,w;
}e[N];

bool operator < (Edge a,Edge b)
{
	return (a.v-a.u)<(b.v-b.u);
}

const int V=N,E=N*10;

int n,m,k,S,T;
int maxr[N],fa[N];
int cnt=1,head[V],cur[V],to[E],nxt[E],c[E];
int num[V];

void adde(int u,int v,int ci)
{
	to[++cnt]=v;
	c[cnt]=ci;
	nxt[cnt]=head[u];
	head[u]=cnt;
	
	to[++cnt]=u;
	c[cnt]=0;
	nxt[cnt]=head[v];
	head[v]=cnt;
}

queue<int>q;

bool bfs()
{
	memcpy(cur,head,sizeof(cur));
	memset(num,-1,sizeof(num));
	q.push(S);
	num[S]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(c[i]&&num[v]==-1)
			{
				num[v]=num[u]+1;
				q.push(v);
			}
		}
	}
	return num[T]!=-1;
}

int dfs(int u,int minflow)
{
	if(!minflow||u==T) return minflow;
	int preflow=0,nowflow;
	for(int i=cur[u];i;i=nxt[i])
	{
		cur[u]=i;
		int v=to[i];
		if(num[v]==num[u]+1&&(nowflow=dfs(v,min(minflow-preflow,c[i]))))
		{
			preflow+=nowflow;
			c[i]-=nowflow;
			c[i^1]+=nowflow;
			if(!(minflow-preflow)) break;
		}
	}
	return preflow;
}

int dinic()
{
	int maxflow=0;
	while(bfs())
		maxflow+=dfs(S,INF);
	return maxflow;
}

bool used[N];

int main()
{
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++)
		e[i].u=read(),e[i].v=read(),e[i].w=read();
	sort(e+1,e+m+1);
	e[++m]=(Edge){1,n,114514};
	S=m,T=m+1;
	for(int i=1;i<=m;i++)
	{
		int l=e[i].u,r=e[i].v;
		bool flag=1;
		for(int nl=l;nl<r;)
		{
			if(!maxr[nl])
			{
				flag=0;
				break;
			}
			nl=e[maxr[nl]].v;
			assert(nl<=r);
		}
		if(flag)
		{
			for(int nl=l;nl<r;)
			{
				adde(i,maxr[nl],e[maxr[nl]].w);
				adde(maxr[nl],i,INF);
				fa[maxr[nl]]=i;
				nl=e[maxr[nl]].v;
				assert(nl<=r);
			}
		}
		else adde(i,T,INF);
		maxr[l]=i;
	}
	used[m]=1;
	for(int i=m-1;i>=1;i--) used[i]=used[fa[i]];
	for(int i=1;i<=k;i++)
	{
		int a=read(),b=read();
		int aa=fa[maxr[a]],bb=fa[maxr[b]];
		if(!used[aa]&&!used[bb]) continue;
		else if(used[aa]&&used[bb])
		{
			adde(aa,bb,INF);
			adde(bb,aa,INF);
		}
		else if(used[aa]) adde(aa,T,INF);
		else if(used[bb]) adde(bb,T,INF);
	}
	int ans=dinic();
	if(ans==INF) puts("-1");
	else printf("%d\n",ans);
	return 0;
}
/*
4 4 1
1 2 10
2 3 100
3 4 100
2 4 10
2 3
*/
/*
11 15 1
1 2 2
2 3 2
3 4 2
4 5 2
5 6 2
6 7 2
7 8 2
8 9 2
9 10 2
10 11 2
1 3 5
3 5 3
5 7 5
7 9 3
9 11 5
5 3
*/
posted @ 2022-10-30 12:19  ez_lcw  阅读(174)  评论(0编辑  收藏  举报