BZOJ 1920 Luogu P4217 [CTSC2010]製品販売(費用流シミュレーション、ツリーライン)

トピックリンク

(bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=1920
(luogu)https://www.luogu.org/problem/P4217

問題の解決策

費用流シミュレーション。
まず、このような構成は、以下の図もよい:
各日について\(Iは\)点を構築するために、別の新しいソースとシンク\(S、T \)

(1)\(S \)する(Iは\)\接続((D_I、0)\)\を(線によって表されます)

(2)\(私は\)する(I + 1 \)\ても\((+ \ INF、C_I)\) 遅延注文)

(3)\(I + 1 \)する\(iは\)であっても\((+ \ INF、M_I)\) 事前に製品注文の遅れに相当)

(4)\(私は\)する(T \)\接続\((U_i、P_I)\) 製造された製品)
、すなわちに接続されたソースを有する側全流量の場合に最小値を求める(最小コスト最大流量を求めます料金)。

:シミュレーション方法を検討する
各短絡から選択されていない場合、グローバル最適を増強するが、任意の順序及び各ソース列挙子に、点光源の縁逆流を接続しないであろう-二つの重要な特性は、フローコスト側は、拡張ポイントがストリームに接続する必要がありました、その答えは正しいです。
ストリームの建設のコストを考慮して、我々は与える(各側縁\(I \)\(I 1 \)+側との間)は逆側の確立、右側が前方エッジの重みの逆です。
仮定\(B \)である側(I + 1 RIGHTARROW iが\ \を 、iは+ 1 \ iは\ RIGHTARROW) 場合、エッジ重みの\が(私+ 1 RIGHTARROW \ \)は流量がないです(\ 0を\)ときは、\は、(i + 1 \)を流れる\(私は\)最短経路を見つけるための時間が、そのコスト行きます\( - \)与えながら、サイドを(\を私は+ RIGHTARROW \します1 \)フロー\( - 1 \) 流れになるまで\(0 \)をこれまで。\(iは+ 1 \ RIGHTARROW \) 共感の側面。
ここでは非常に素晴らしいアイデアがあります-増補左から右へ。
左から右へと各点光源が接続されてからの列挙は、それぞれ、獲得する試みは、広い左右の増大、より小さな置換の一価。
右へ増大するとき、最長パスの開始点を見つけ、その後、現在のポイントの残りのトラフィックフローとする最長の道路のその点計算\(T \)残りのトラフィックの最小値。
我々は右の増補場合は\(X \)のトラフィックを、道路は右から左サイドへの逆流で増補各ポイントの増加につながります\(のx \) 。
左の増強各クロス理由側は、最初に右へ増加した流れの裏面に、その左側増大点を「閾値」を有する、流れる辺の重みが負である閾値に等しい未満である、閾値エッジ重みより大きくは正である(注このしきい値は、過去を流れることができるしきい値ストリームよりも異なる容量、である!ここで私が間違って何回かあった)、私たちは変わらず、したがって、増補流れをすべてコラージュの権利を確保することで、拡張1-時間を考慮します最長経路に流れ、現在のポイント、その点を残り\(T \)最小流量の残りの3つ、道路点閾値に最大電流点。
私たちが増強ままの場合\(X \)のトラフィックを、それが道路の両側の削減につながるしきい値増補\(X \)をしきい値になる場合は、エッジのこの時間は(0 \)\、あなたは、このエッジのしきい値を変更したいです。
最後に、もしと拡張プロセス\(T \)の流れになるに接続点(0 \)\だけでなく、それを削除します。
だから我々は、操作データの支持構造を維持(下の具体的なメンテナンス方法を繰り返す)したいです。
複雑さの以下の分析は、美容増強左から右に、それが閾値をまたがる低減されたときに適切な場所を増強増強点の左側の側縁の増加されたことを確認することができることである\を( 0 \)番号は変更の右側には、多くの場合、数回で、しばしば数倍です。
拡張原因各操作の総数は線形であるので、エッジフローアウト、またはシンクエッジフローアウト、または右側コラージュ変化に接続された1つのソースに接続されています。

最後に、データ構造のメンテナンスを使用する方法を検討してください。
オープン3つのツリーラインは、メンテナンスコストは、左の費用と中間側縁部の右増補流れを増強しました。
第2セグメントツリー木右増強の右側が変化しないので、(このように削除をサポートするために、左右の右側に右端の点に直接ポイントを維持し、右のメンテナンスコストを増大代わりに\(+ \ INF \) )、クエリとその位置の最小値。
同じことが、左、右及び右から左端の点の各点は、クエリおよび最小位置を、変更、削除サポートセグメントツリーのメンテナンスについても同様です。
第3のセグメントツリーツリーは、各側縁の流れを維持します。まず、プラスクエリおよび最小位置の範囲、そして最も難しい操作をサポートするために-最初の位置のすべてがさ(0 \)\いつでも後の最初の増減後の任意の位置になった場合には、\(0 \ )、その後すぐに、セグメントツリー上のイベントが発生し、これらの位置、および対応する動作を列挙します。私は、最小間隔の息子の場合、このメソッドは、プッシュダウンタイムで使用\(0 \) その後、再帰的に息子あなたがリーフノードを見つけるまで、ゼロのリーフノードをするために割り当てられ、(\ + \ infファイル)\しますそして木のセグメント、および対応する動作。しかし、困難な問題は、初期値を区別する方法であり、\(0 \)となっ修正後の最初の増加が減少した後、\(0 \) 私は値がされていない場合、マークの使用を開始(\ 0 \)に変更と考え、戻っていくつかのマークの変更後の位置を変更することが可能であるので、これは、間違っている\(0 \)ので、私は一人で新しいタグに記録した。\を(F \)変更されたか否かを示します。場合にのみ\(F \)が真であるとの値\(0 \)は、対応する動作を行います。

時間複雑\(O(N \ N-ログ)\)

(私は私の言葉遣いが悩み...... QAQを持っていると思います)

コード

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<utility>
#include<cassert>
#define llong long long
#define pli pair<llong,int>
#define mkpr make_pair
using namespace std;
 
void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
 
const int N = 1e5;
const llong INF = 10000000000000ll;
llong a[N+3],b[N+3],w1[N+3],w2[N+3],c[N+3];
llong dl[N+3],dr[N+3];
int n;
 
struct SegmentTree1
{
    struct SgTNode
    {
        llong tag; pli mini;
        SgTNode() {mini = mkpr(INF,0); tag = 0;}
    } sgt[(N<<2)+3];
    void update(pli &x,pli y) {if(y.first<x.first) x = y;}
    void pushdown(int u)
    {
        assert(u<(N<<2));
        llong tag = sgt[u].tag;
        if(tag)
        {
            sgt[u<<1].mini.first += tag; sgt[u<<1].tag += tag;
            sgt[u<<1|1].mini.first += tag; sgt[u<<1|1].tag += tag;
            sgt[u].tag = 0;
        }
    }
    void pushup(int u)
    {
        assert(u<(N<<2));
        sgt[u].mini = mkpr(INF,0);
        update(sgt[u].mini,sgt[u<<1].mini);
        update(sgt[u].mini,sgt[u<<1|1].mini);
    }
    void build(int u,int le,int ri,llong a[])
    {
        assert(u<(N<<2));
        if(le==ri) {sgt[u].mini = mkpr(a[le],le); return;}
        int mid = (le+ri)>>1;
        build(u<<1,le,mid,a);
        build(u<<1|1,mid+1,ri,a);
        pushup(u);
    }
    void addval(int u,int le,int ri,int lb,int rb,llong x)
    {
        assert(u<(N<<2));
        if(le>=lb && ri<=rb) {sgt[u].mini.first += x; sgt[u].tag += x; return;}
        pushdown(u);
        int mid = (le+ri)>>1;
        if(lb<=mid) addval(u<<1,le,mid,lb,rb,x);
        if(rb>mid) addval(u<<1|1,mid+1,ri,lb,rb,x);
        pushup(u);
    }
    pli querymin(int u,int le,int ri,int lb,int rb)
    {
        assert(u<(N<<2));
        if(le>=lb && ri<=rb) {return sgt[u].mini;}
        pushdown(u);
        int mid = (le+ri)>>1; pli ret = mkpr(INF,0);
        if(lb<=mid) update(ret,querymin(u<<1,le,mid,lb,rb));
        if(rb>mid) update(ret,querymin(u<<1|1,mid+1,ri,lb,rb));
        pushup(u);
        return ret;
    }
} sgt1,sgt2;
 
struct SegmentTree2
{
    struct SgTNode
    {
        pli mini; llong tag; bool f;
        SgTNode() {mini = mkpr(0,0); tag = 0ll;}
    } sgt[(N<<2)+3];
    void update(pli &x,pli y) {if(y.first<x.first) x = y;}
    void pushup(int u)
    {
        assert(u<(N<<2));
        sgt[u].mini = mkpr(INF,0);
        update(sgt[u].mini,sgt[u<<1].mini);
        update(sgt[u].mini,sgt[u<<1|1].mini);
    }
    void pushdown(int u,int le,int ri)
    {
        assert(u<(N<<2));
        llong tag = sgt[u].tag;
        if(sgt[u].f)
        {
            int mid = (le+ri)>>1;
            if(le!=ri)
            {
                sgt[u<<1].mini.first += tag; sgt[u<<1].tag += tag; sgt[u<<1].f = true;
                sgt[u<<1|1].mini.first += tag; sgt[u<<1|1].tag += tag; sgt[u<<1|1].f = true;
            }
            sgt[u].tag = 0;
            if(sgt[u].mini.first!=0) return;
            if(le==ri) {sgt1.addval(1,1,n,1,le,b[le]+a[le]); sgt[u].mini.first = INF; return;}
            if(sgt[u<<1].mini.first==0) {pushdown(u<<1,le,mid);}
            if(sgt[u<<1|1].mini.first==0) {pushdown(u<<1|1,mid+1,ri);}
            pushup(u);
        }
    }
    void addval(int u,int le,int ri,int lb,int rb,llong x)
    {
        assert(u<(N<<2));
        if(le>=lb && ri<=rb) {sgt[u].mini.first += x; sgt[u].tag += x; sgt[u].f = true; pushdown(u,le,ri); return;}
        pushdown(u,le,ri); int mid = (le+ri)>>1;
        if(lb<=mid) addval(u<<1,le,mid,lb,rb,x);
        if(rb>mid) addval(u<<1|1,mid+1,ri,lb,rb,x);
        pushup(u);
    }
    pli querymin(int u,int le,int ri,int lb,int rb)
    {
        assert(u<(N<<2));
        if(le>=lb && ri<=rb) {return sgt[u].mini;}
        pushdown(u,le,ri); int mid = (le+ri)>>1; pli ret = mkpr(INF,0);
        if(lb<=mid) update(ret,querymin(u<<1,le,mid,lb,rb));
        if(rb>mid) update(ret,querymin(u<<1|1,mid+1,ri,lb,rb));
        pushup(u);
        return ret;
    }
} sgt3;
 
int main()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++) scanf("%lld",&w1[i]);
    for(int i=1; i<=n; i++) scanf("%lld",&w2[i]);
    for(int i=1; i<=n; i++) scanf("%lld",&c[i]);
    for(int i=1; i<n; i++) scanf("%lld",&b[i]);
    for(int i=1; i<n; i++) scanf("%lld",&a[i]);
    dl[1] = 0; for(int i=2; i<=n; i++) dl[i] = dl[i-1]+a[i-1];
    dr[n] = 0; for(int i=n-1; i>=1; i--) dr[i] = dr[i+1]-a[i];
    for(int i=1; i<=n; i++) dl[i]=dl[i]+c[i],dr[i]=dr[i]+c[i];
    sgt1.build(1,1,n,dr);
    sgt2.build(1,1,n,dl);
    for(int i=1; i<=n; i++) dl[i]-=c[i],dr[i]-=c[i];
    llong ans = 0ll;
    for(int i=1; i<=n; i++)
    {
        while(w1[i]>0)
        {
            pli vl = mkpr(INF,0),vr = mkpr(INF,0);
            pli tmp = sgt1.querymin(1,1,n,1,i);
            vl = mkpr(tmp.first-dr[i],tmp.second);
            tmp = sgt2.querymin(1,1,n,i,n);
            vr = mkpr(tmp.first-dl[i],tmp.second);
            if(vl.first<vr.first)
            {
                llong flow = min(w1[i],w2[vl.second]);
                if(vl.second<i)
                {
                    tmp = sgt3.querymin(1,1,n,vl.second,i-1);
                    flow = min(flow,tmp.first);
                    sgt3.addval(1,1,n,vl.second,i-1,-flow);
                }
                ans += flow*vl.first;
                w1[i] -= flow;
                w2[vl.second] -= flow;
                if(w2[vl.second]==0)
                {
                    sgt1.addval(1,1,n,vl.second,vl.second,INF);
                    sgt2.addval(1,1,n,vl.second,vl.second,INF);
                }
            }
            else
            {
                llong flow = min(w1[i],w2[vr.second]);
                ans += flow*vr.first;
                if(i<vr.second)
                {
                    sgt3.addval(1,1,n,i,vr.second-1,flow);
                }
                w1[i] -= flow;
                w2[vr.second] -= flow;
                if(w2[vr.second]==0)
                {
                    sgt1.addval(1,1,n,vr.second,vr.second,INF);
                    sgt2.addval(1,1,n,vr.second,vr.second,INF);
                }
            }
        }
        if(sgt3.querymin(1,1,n,i,i).first==0)
        {
            sgt3.addval(1,1,n,i,i,INF);
            sgt1.addval(1,1,n,1,i,b[i]+a[i]);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

おすすめ

転載: www.cnblogs.com/suncongbo/p/11374733.html