预流推进·重标号/HLPP
思路
通过不断 拔高 每个点的高度,使得 贮存 在某个点的水流向周边高度更低的点。
过程
-
一开始,对于初始源点,我们将其高度拔高到 \(n\),其他点为 到汇点的距离(这实际上是优化的部分)。那么源点连出的点都会有边权大小的水。
-
之后,我们把所有存贮着水的点中,让高度最高的那个点,尝试向周边流出水。这里不同于一开始源点向外流,只能是每个点向高度比自己小 \(1\) 的点流出。在一次流出后,如果这个点还有存贮着未流出的水,那么则表明这个点的高度需要更改,我们将其改为 剩余可以流出的边中指向的点的最小高度+1,这样下一次就至少能有一些流流出去。
优化
\(1.\) 对于当前流出点 \(i\),如果它流量还有剩,并且该高度下 不存在在队内的点,那么它就是 当前有存贮着水的最高的点。此时可以将所有高度大于 \(h_i\) 且小于 \(n+1\) 的 非源点汇点 的高度设为 \(n+1\)。也就是说,它们就是回流点的第一批。
\(2.\) 我们又注意到,第一个优化的实质,是将某些点设置为将流量流回源点的 流回点。那么,很明显,我们没有必要继续维护这些点的信息了,因为所有流经它们的水只会流回源点,而不会影响到我们所要求的终点的答案。我们可以将它们打上标记,直接丢掉。
\(3.\) 当前弧优化。这个我还有点懵,让我思考一会儿 \(\cdots\)
\(4.\) 定期重新跑 bfs 来设定高度,就像一开始跑的那样,注意不要把优化 \(2\) 中被丢弃的点拉进来跑。
\(5.\) 考虑到每次更改高度只最多更改一个点,并且是增大,所以可以用链表而不是优先队列。
看情况加吧,这几个优化都不是很加码量。
下面附一个加了 \(1,2\) 优化的代码,已经足以应付大部分题目了。
#include <queue>
#include <stdio.h>
#include <string.h>
#define LL long long
using namespace std;
inline LL rin()
{
LL s=0;
bool bj=false;
char c=getchar();
for(;(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')bj=true,c=getchar();
for(;c>='0'&&c<='9';c=getchar())s=(s<<1)+(s<<3)+(c^'0');
if(bj)return -s;
return s;
}
const int N=1.3e3+3;
const int M=1.3e5+3;
const int INF=0x3f3f3f3f;
int n,m;
int S,T;
struct gyq
{
int to,nxt,v;
}a[M<<1];
int nam=1;
int hea[N];
int v[N];
int h[N];
inline void add(int x,int y,int z)
{
a[++nam].nxt=hea[x];a[nam].to=y;a[nam].v=z;hea[x]=nam;
a[++nam].nxt=hea[y];a[nam].to=x;a[nam].v=0;hea[y]=nam;
return;
}
queue<int>d;
inline bool bfs()
{
for(int i=1;i<=n;i++)h[i]=INF;
for(d.push(T),h[T]=0;!d.empty();)
{
int now=d.front();d.pop();
for(int i=hea[now];i;i=a[i].nxt)if(a[i^1].v&&h[now]+1<h[a[i].to])h[a[i].to]=h[now]+1,d.push(a[i].to);
}
return (h[S]!=INF);
}
struct myru_int{inline bool operator ()(int x,int y)const {return h[x]<h[y];}};
priority_queue<int,vector<int>,myru_int>q;
bool tag[N];
int cutt[N<<1];
inline void water(int st,int i,int v_)
{
a[i].v-=v_;a[i^1].v+=v_;
v[st]-=v_;v[a[i].to]+=v_;
if(!tag[a[i].to])q.push(a[i].to),tag[a[i].to]=true;
return;
}
inline int hlpp()
{
if(!bfs())return 0;
tag[S]=tag[T]=true;
h[S]=n;
for(int i=hea[S];i;i=a[i].nxt)water(S,i,a[i].v);
for(int i=1;i<=n;i++)if(h[i]!=INF)cutt[h[i]]++;
for(;!q.empty();)
{
int now=q.top();
q.pop();tag[now]=false;
for(int i=hea[now];i;i=a[i].nxt)
{
if(h[a[i].to]+1!=h[now]||!a[i].v)continue;
water(now,i,min(a[i].v,v[now]));
if(!v[now])break;
}
if(v[now])
{
cutt[h[now]]--;
if(!cutt[h[now]])for(int i=1;i<=n;i++)if(h[i]>h[now]&&h[i]<=n&&i!=S&&i!=T)h[i]=n+1,tag[i]=true;
h[now]=INF;for(int i=hea[now];i;i=a[i].nxt)if(a[i].v&&h[a[i].to]+1<h[now])h[now]=h[a[i].to]+1;
if(h[now]>n)tag[now]=true;else q.push(now),cutt[h[now]]++;
}
}
return v[T];
}
int main()
{
n=rin();m=rin();S=rin();T=rin();
for(int i=1;i<=m;i++){int x=rin(),y=rin(),v=rin();add(x,y,v);}
printf("%d\n",hlpp());
return 0;
}
$$\texttt{Dirty Deeds Done Dirt Cheap}$$

浙公网安备 33010602011771号