题意:给一张平面图,满足这张平面图的对偶图是一棵树,有若干限制,形如 “若经过点 \(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
*/
标签:int,平面图,num,lca,INF,XSY3396,对偶
From: https://www.cnblogs.com/ez-lcw/p/16840974.html