Wannaflyキャンプ2020日3F協会マネジメント - 意思決定単調DP、全体の半分

そこ\(\ N-)からなる配列番号\(a_iを}は{\) に分割する\(K \)右セグメントの各セグメントに対して、これが定義されているに((i、j)を\ \ ST \ I <J、\ = a_iを a_j \) 数、各セグメントの重量と最小ようにプログラムを分割する方法を見つけます。\(N \ 10 ^ 5当量 、K \当量分(N、20)、a_iを\当量のn \)

セット\(F [I] [J ] \) を示して\(A_ {1..j} \)\(I \)セグメントの最小値は、伝達方程式を得ることは容易である
\ [F [i]が[ J] = \分(F [
I-1] [K] +コスト(K + 1、J))\ \ \(K <J)\] 激しい転送である\(O(N ^ 2 K)\)ここでは意思決定単調、すなわち\(p_j \)\(F [I] [J ] \) 最適な転写ポイントのは、次いで任意ため、(より多くの一番左を取ることがある場合)\(K <jは\)でなければなりません(p_k \当量p_j \)\

注:フォーム\(F_iと=分/ MAX_ {J ^ = {I} 1. 1- G_J} + {W_ I、J} \) 表記\(F_iと\)最適識別点の\(P_I \)すなわち\(F_iと\)から(G_ {P_I} + W_ \ {I、P_I} \) の最適な転送において、満足する場合\(P_I \のLeq P_ {I}。1 + \) 判定満たす方程式と呼ばれます単調

(私は暴力はプレイテーブルを書くことと、その後法律を見ることができることを証明する必要はありません)

私たちは移転を加速するために、全体の半分を持っているので、

我々は現在、いくつかの間隔解決するために検討されている([L、R&LT] \)\、全て\(F_ {I、J} 、\ [L、R]でJ \ \) 最適識別点のを([L、R] \ )\の間。

以下のための\([L、R] \ ) 中間\(MID \) 暴力にわたって掃引することができる\([L、R&LT] \)を、最適な決定点を見つける(K \)\

次いで、決定単調、全てので\(F_ {I、J} 、\にJ \ [L、中間] \) の決定に当たる([L、K] \ \ ) 全てに\(F_ {I、J }、\ [半ばにおけるJ \ + 1、R] \) の決定は、上に落ちる\([K、R] \ ) に

全体の時間複雑\(O(KN \ nはログ )\)

その後、サイクル境界とはTLEの幅を書いたので

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1000005;
int n,m,f[N],g[N],a[N],buc[N]={1,0},cnt;
signed pl,pr;

inline void push(signed x) {
    cnt+=buc[x];
    buc[x]++;
}

inline void pop(signed x) {
    buc[x]--;
    cnt-=buc[x];
}

void trans(signed l,signed r) {
    while(l<pl) pl--, push(a[pl]);
    while(r>pr) pr++, push(a[pr]);
    while(l>pl) pop(a[pl]), pl++;
    while(r<pr) pop(a[pr]), pr--;
    //cout<<l<<" "<<r<<" "<<cnt<<endl;
}

void solve(int l,int r,int L,int R) {
    int mid=(l+r)/2;
    int mx=1e18,k=0;
    for(int i=L;i<=min(R,mid-1);i++) { //!
        trans(i+1,mid);
        if(g[i]+cnt<mx) {
            mx=g[i]+cnt;
            k=i;
        }
    }
    f[mid]=mx;
    if(l<r) {
        solve(l,(l+r)/2-1,L,k); //!
        solve((l+r)/2+1,r,k,R);
    }
}

signed main() {
    ios::sync_with_stdio(false);
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=n;i++) {
        trans(1,i);
        f[i]=g[i]=cnt;
        //<<g[i]<<" ";
    }//cout<<endl;
    for(int i=2;i<=m;i++) {
        solve(1,n,1,n);
        for(int j=1;j<=n;j++) g[j]=f[j];
        /*for(int j=1;j<=n;j++) {
            cout<<f[j]<<" ";
        }
        cout<<endl;*/
    }
    cout<<f[n];
}

おすすめ

転載: www.cnblogs.com/mollnn/p/12340241.html