[bzoj4658]rescue

题目描述

wyh8000很喜欢看书,特别是那种很容易死脑细胞的书。
wyh8000看书喜欢从第K页开始看起,然后看到第M页,但是wvh8000并不是有耐心的小盆友,他
只想快点完成看书任务,然后就可以去愉快的农别人了,于是他经常跳着看,但是他一次最多跳D页,
然后阅读那一页的内容,然后死掉A的脑细胞。当然如果那一页的内容他比较感兴趣,又会回复一定
的脑细胞。
好心的学长不希望看到wyh8000的脑细胞死光,你能帮助wvh8000死掉尽可能少的脑细胞吗?

DP

关键点才有用嘛……
设f[i]表示看第i个关键点死的最少脑细胞。
f[i]=min(f[j]+TiTjDA)bi
这个DPn^2
然后该往哪个方向思考呢?你注意两个T同时+D相对关系还是不变,看来和模有关了。
把Ti表示为Ci*D+Ei( 0<=Ei<D
然后发现原式变成
f[i]=min(f[j]+(CiCj+EiEjD)A)bi
只有Ei>Ej才产生1的贡献。
好咯,根据E为下标维护线段树即可。

#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10,maxtot=3500000+10,maxd=1e9;
ll left[maxtot],right[maxtot];
ll tree[maxtot];
ll T[maxn],b[maxn],c[maxn],e[maxn];
ll f[maxn];
ll i,j,k,l,t,n,m,tot,root,A,D;
ll read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void change(ll &x,ll l,ll r,ll a,ll b){
    if (!x) x=++tot,tree[x]=1e18;
    if (l==r){
        tree[x]=min(tree[x],b);
        return;
    }
    ll mid=(l+r)/2;
    if (a<=mid) change(left[x],l,mid,a,b);else change(right[x],mid+1,r,a,b);
    tree[x]=min(tree[left[x]],tree[right[x]]);
}
ll query(ll x,ll l,ll r,ll a,ll b){
    if (!x||a>b) return 1e18;
    if (l==a&&r==b) return tree[x];
    ll mid=(l+r)/2;
    if (b<=mid) return query(left[x],l,mid,a,b);
    else if (a>mid) return query(right[x],mid+1,r,a,b);
    else return min(query(left[x],l,mid,a,mid),query(right[x],mid+1,r,mid+1,b));
}
int main(){
    k=read();m=read();D=read();A=read();n=read();
    fo(i,1,n) T[i]=read(),b[i]=read();
    T[0]=k;T[++n]=m;
    fo(i,0,n){
        c[i]=T[i]/D;
        e[i]=T[i]%D;
    }
    tree[0]=1e18;
    change(root,0,D-1,e[0],-c[0]*A);
    fo(i,1,n){
        f[i]=1e18;
        j=query(root,0,D-1,0,e[i]-1);
        f[i]=min(f[i],j+A+c[i]*A-b[i]);
        j=query(root,0,D-1,e[i],D-1);
        f[i]=min(f[i],j+c[i]*A-b[i]);
        change(root,0,D-1,e[i],f[i]-c[i]*A);
    }
    printf("%lld\n",-f[n]);
}

你可能感兴趣的:(一般动规与递推,线段树)