其他分享
首页 > 其他分享> > P3515 [POI2011]Lightning Conductor 四边形不等式

P3515 [POI2011]Lightning Conductor 四边形不等式

作者:互联网

P3515 [POI2011]Lightning Conductor 四边形不等式

链接

1 讲解

按道理说这不应该算一个 dp 了,但是满足决策单调性。

首先先把绝对值去掉,因为如果 \(j\) 比 \(i\) 大我们可以反着做一遍。以下认为 \(j\le i\) 。

设 \(w(j,i)=\sqrt{i-j}\) ,我们把不等式移项后可以得到:

\[p\ge a_j+\sqrt{i-j}+a_i=a_j+w(j,i)+a_i \]

我们证明 \(w\) 满足四边形不等式:

不难发现,这与我们的四边形不等式符号想法,但是这个题,很明显有:

\[p_i=\max\{a_j+\sqrt{i-j} \}-a_i \]

注意这里是取最大值而不是四边形不等式里取最小值,所以这个东西仍然可以证明满足决策单调性,只需要像我们证明满足四边形不等式就满足取 \(\min\) 的决策单调性是一样的。

2 代码实现

在这里使用单调队列作为二分栈来做的,对于每一个决策,维护一个区间,表示在这个区间里这个决策最优。

注意到在这个题中,我们要保证 \(w\) 要有定义,同时因为自己本身也是一个决策,所以要先入队,在决策。

剩下的我们看着代码来讲解。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define int long long
#define ull unsigned long long
#define N 500010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){
    return a>b?a:b;
}

struct node{
    int k,l,r;
    inline node(){}
    inline node(int k,int l,int r) : k(k),l(l),r(r) {}
};
node q[N];

int n,a[N],head,tail;
dd f[N],sqr[N];

inline dd calc(int id,int k){
    return (dd)a[k]+sqr[id-k];
}

inline int erfen(int k,int l,int r){
    while(l<r){
        int mid=(l+r)>>1;
        if(calc(mid,q[tail].k)>calc(mid,k)) l=mid+1;
        else r=mid;
    }
    return l;
}

inline void insert(int k){
    if(head==tail){q[++tail]=node(k,1,n);return;}
    int pos;
    while(head<tail&&q[tail].l>=k&&calc(q[tail].l,q[tail].k)<calc(q[tail].l,k)){
        tail--;
    }
    if(q[tail].r>=k&&calc(q[tail].r,q[tail].k)>calc(q[tail].r,k)){
        if(q[tail].r!=n) pos=q[tail].r+1;
        else return;
    }
    else pos=erfen(k,Max(q[tail].l,k),q[tail].r);
    q[tail].r=pos-1;q[++tail]=node(k,pos,n);
}

inline void solve(int e){
    head=tail=0;insert(1);
    for(int i=1;i<=n;i++){
        if(head<tail&&q[head+1].r<=i-1) head++;
        if(head<tail){
            int k=q[head+1].k;
            f[i]=Max(f[i],calc(i,k));
        }
        if(i!=n) insert(i+1);
    }
}

signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) {scanf("%lld",&a[i]);sqr[i]=sqrt(i);} 
    solve(1);
    reverse(a+1,a+n+1);reverse(f+1,f+n+1);
    solve(2);
    for(int i=n;i>=1;i--) printf("%lld\n",(int)ceil(f[i])-a[i]);
    return 0;
}

每次我们把决策点在 \(i\) 左边的决策去掉,这样每次就可以直接取出队首来转移,insert 函数支持插入一个决策。

首先如果队列为空的话,直接把决策加进去。否则,我们在序列左侧有意义的情况下检查并判断是否要弹出队尾。

我们检查当前插入决策是否比队尾决策要劣,如果在最右边还劣的话,根据决策单调性,当前决策只能去覆盖后边。

否则我们在这段里面二分,注意维护 \(w\) 函数是否有意义。

变量千万千万不能写重 !!!

标签:Conductor,int,POI2011,决策,sqrt,Lightning,tail,inline,define
来源: https://www.cnblogs.com/TianMeng-hyl/p/14968300.html