预流推进·重标号/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;
}
posted @ 2021-02-02 16:22  zjjws  阅读(118)  评论(0)    收藏  举报