JZOJ5646. 【NOI2018模拟4.12】染色游戏

题目

这里写图片描述
这里写图片描述

题目大意

给出ai,要求从中选择一些ai出来,可以不选(一定要注意)
使得这些ai是严格上升。
对于每一段连续的空位,都要付出一些代价。
要求最大化选出来的ai之和减去总代价。

题解

一种n2的做法,
fi表示ai必选的最大答案。
fi=maxfj+(ij(ij1)/2+ai(aj<ai)
在这种做法上面再进行优化,
为了去除aj<ai,就先按照a排一次序。
按照a从小到大的顺序来处理,
对于每一个fi能影响到的,是在它原来位置在它后面的还要a比它大的。
化简上面的式子,就可以发现每个fi对后面的影响就是一个一次函数。
那么就以原来序列的下标建一棵线段树,用来维护线段。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 1000003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(ll &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    ll v;
    int id;
}a[N];

struct arr
{
    ll k,b;
    int l,r;
}tr[N*10],w;

ll n,m,f[N],ans,v,id,t,tot;
int opl,opr;

bool cmp(node a,node b)
{
    return a.v<b.v || (a.v==b.v && a.id<b.id);
}

void ins(int x,int l,int r)
{
    ll t1=w.k*l+w.b,t2=w.k*r+w.b;
    ll w1=tr[x].k*l+tr[x].b,w2=tr[x].k*r+tr[x].b;
    if((t1>=w1 && t2>=w2))
    {
        tr[x].k=w.k;
        tr[x].b=w.b;
        return;
    }
    if(l==r || (t1<w1 && t2<w2))return;
    int m=(l+r)>>1;
    if(!tr[x].l)tr[x].l=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
    if(!tr[x].r)tr[x].r=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
    ins(tr[x].l,l,m);
    ins(tr[x].r,m+1,r);
}

void work(int x,int l,int r)
{
    if(opl<=l && r<=opr)
    {
        ins(x,l,r);
        return;
    }
    int m=(l+r)>>1;
    if(opl<=m)
    {
        if(!tr[x].l)tr[x].l=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
        work(tr[x].l,l,m);
    }
    if(m<opr)
    {
        if(!tr[x].r)tr[x].r=++tot,tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;
        work(tr[x].r,m+1,r);
    }
}

ll find(int x,int l,int r,int pos)
{
    if(x==0)return 0;
    if(l==r)return pos*tr[x].k+tr[x].b;
    int m=(l+r)>>1;
    if(pos<=m)return max(pos*tr[x].k+tr[x].b,find(tr[x].l,l,m,pos));
        else return max(pos*tr[x].k+tr[x].b,find(tr[x].r,m+1,r,pos));
}

int main()
{
    freopen("paint.in","r",stdin);
    freopen("paint.out","w",stdout);

    read(n);
    for(int i=1;i<=n;i++)
        read(a[i].v),a[i].id=i;

    sort(a+1,a+1+n,cmp);
    memset(f,128,sizeof(f));ans=-n*(n+1)/2;
    tot=1;tr[tot].b=tr[tot].k=tr[tot].l=tr[tot].r=0;

    for(int i=1;i<=n;i++)
    {
        id=a[i].id;v=a[i].v;

        t=find(1,1,n,id);
        f[id]=t+v-id*(id-1)/2;
        w.k=id;w.b=f[id]-id*(id+1)/2;
        opl=id;opr=n;work(1,1,n);//ins(1,1,n);

        ans=max(ans,f[id]-(n-id)*(n-id+1)/2);
    }

    if(ans<0)P('-'),ans=-ans;
    write(ans);

    return 0;
}
阅读更多
文章标签: 线段树 dp
个人分类: 题解 DP 线段树
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭