题意:给一张平面图,满足这张平面图的对偶图是一棵树,有若干限制,形如 “若经过点 x x x 则必须要经过点 y y y”,求 1 ∼ n 1\sim n 1∼n 的最短路。
由于平面图与其对偶图互为对偶,所以平面图最短路等于其对偶图上的最小割。
注意这里的最小割和普通的最小割有点不一样,这里要求任意一条 S → T S\to T S→T 的路径上都只有一条割边(不然就不能对应平面图上的一条最短路),于是我们在求最小割时要给每条边加上容量为 INF 的反边。
例:
假设对偶图左图长这样(尽管这不是一棵树),那么我们的最小割算法会把 A A A 划分到 T T T 集, B B B 划分到 S S S 集,那么这样割掉的边为 ( S , A ) (S,A) (S,A) 和 ( B , T ) (B,T) (B,T),此时得到最小割 2 2 2,如中图。
但你会发现这样就有一条从 S → T S\to T S→T 的路径上有两条边被割掉了(如右图中蓝色路径),是不合法的。
所以我们需要对每一条边都连一条容量为 INF 的反边,以确保任意一条 S → T S\to T S→T 的路径上都只有一条割边(否则就必然会割掉某条 INF 反边,显然最小割不是这个)。
而对于题目的限制,我们先考虑 “平面图上经过了点 i i i” 在对偶图上对应着什么:
如图,设以 i i i 为右端点的最长的线段所对应的节点为 m a x l i maxl_i maxli,以 i i i 为左端点的最长的线段所对应的节点为 m a x r i maxr_i maxri,容易证明 m a x l i maxl_i maxli 和 m a x r i maxr_i maxri 的父亲为同一个点 l c a i lca_i lcai。
那么 “平面图上经过了点 i i i” 等价于如图中蓝色的两条链上有任意一条边被割,发现这等价于 l c a lca lca 被分到 S S S 集:因为蓝色的两条链上有任意一条边被割一定使得 l c a lca lca 被分到 S S S 集,而 l c a lca lca 被分到 S S S 集一定使得蓝色的两条链上某一条边被割(因为不可能割叶子到 T T T 的 INF 边)。
那么对于题目中的一条限制 a , b a,b a,b,就等价于 l c a a lca_a lcaa 和 l c a b lca_b lcab 必须同在 S S S 集或同在 T T T 集。这很好处理,直接在 l c a a lca_a lcaa 和 l c a b lca_b lcab 之间连一条双向 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
*/