洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$

正解:线段树优化$dp$

解题报告:

传送门$QwQ$

难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$

先考虑暴力$dp$?就设$f_{i,j}$表示选的第$j$个基站是$i$的最小费用,就有$f_{i,j}=min(f_{k,j}+cost(k,i))+c_i$,这个$cost$就$[k+1,i-1]$之间所有基站的补偿之和.

发现这个$cost$并不好求?于是逆向思考,每次在决策完选$x$转移完之后就会进入不选$x$的阶段嘛(因为是,$j$在外层循环$i$在内层,也就每次只会选一个基站,所以决策完第$j$个选$x$后就会决策第$j$个选$x$之后的基站,那第$x$个基站就不会被选了$QwQ$),那就把$[1,x]$范围内所有因为不选$x$而导致代价增加的加上

于是先预处理一个$st_{i}$和一个$ed_{i}$数组,表示能让$i$被覆盖的基站的编号范围,然后每次把所有$ed_{i}=x$的点找出来.但是发现当上一次选的点在$[st_{i},ed_{i}-1]$范围内的时候就依然没有关系,所以就只要$f_{[1,st[i]-1],j}+=w_i$

现在就变成了区间加区间查询最小值,线段树维护就好,$over$

洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define gc getchar()
#define t(i) edge[i].to
#define ri register int
#define rc register char
#define rb register bool
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i)
#define e(i,x) for(ri i=head[x];i;i=edge[i].nxt)
#define lb(x) lower_bound(d+1,d+1+n,x)-d

const int N=20000+10,inf=0x3f3f3f3f;
int n,K,d[N],c[N],s[N],w[N],st[N],ed[N],tr[N<<2],tag[N<<2],ed_cnt,head[N],f[N],as;
struct edg{int to,nxt;}edge[N];

il int read()
{
    rc ch=gc;ri x=0;rb y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il void ad(ri x,ri y){edge[++ed_cnt]=(edg){x,head[y]};head[y]=ed_cnt;}
void build(ri nw,ri l,ri r)
{
    tag[nw]=0;if(l==r)return void(tr[nw]=f[l]);
    ri mid=(l+r)>>1;build(nw<<1,l,mid);build(nw<<1|1,mid+1,r);
    tr[nw]=min(tr[nw<<1],tr[nw<<1|1]);
}
il void pushdown(ri nw)
{
    if(!tag[nw])return;
    tag[nw<<1]+=tag[nw];tag[nw<<1|1]+=tag[nw];tr[nw<<1]+=tag[nw];tr[nw<<1|1]+=tag[nw];tag[nw]=0;
}
void add(ri nw,ri l,ri r,ri to_l,ri to_r,ri dat)
{
    if(to_l<=l && r<=to_r){tr[nw]+=dat;tag[nw]+=dat;return;}//return void(tr[nw]+=dat,tag[nw]+=dat);
    pushdown(nw);ri mid=(l+r)>>1;
    if(mid>=to_l)add(nw<<1,l,mid,to_l,to_r,dat);if(mid<to_r)add(nw<<1|1,mid+1,r,to_l,to_r,dat);
    tr[nw]=min(tr[nw<<1],tr[nw<<1|1]);
}
int query(ri nw,ri l,ri r,ri to_l,ri to_r)
{
//    printf("nw=%d l=%d r=%d to_l=%d to_r=%d\n",nw,l,r,to_l,to_r);
    if(to_l<=l && r<=to_r)return tr[nw];
    pushdown(nw);ri mid=(l+r)>>1,ret=inf;
    if(mid>=to_l)ret=query(nw<<1,l,mid,to_l,to_r);if(mid<to_r)ret=min(ret,query(nw<<1|1,mid+1,r,to_l,to_r));
//    printf("[%d,%d]:%d\n")
    return ret;
}

int main()
{
//    freopen("2605.in","r",stdin);//freopen("2605.out","w",stdout);
    n=read();K=read()+1;rp(i,2,n)d[i]=read();rp(i,1,n)c[i]=read();rp(i,1,n)s[i]=read();rp(i,1,n)w[i]=read();++n;d[n]=w[n]=inf;
    rp(i,1,n){st[i]=lb(d[i]-s[i]);ed[i]=lb(d[i]+s[i]);if(d[ed[i]]>d[i]+s[i])--ed[i];ad(i,ed[i]);}
    ri ret=0;rp(i,1,n){f[i]=ret+c[i];e(j,i)ret+=w[t(j)];}as=f[n];
    rp(i,2,K)
    {
        build(1,1,n);
        rp(j,1,n)
        {
            f[j]=(j>i-1?query(1,1,n,i-1,j-1):0)+c[j];//printf("f[%d]=%d=%d+%d\n",j,f[j],j>i-1?query(1,1,n,1,j-1):0,c[j]);
            e(k,j)if(st[t(k)]>1)add(1,1,n,1,st[t(k)]-1,w[t(k)]);
        }
        as=min(as,f[n]);
    }
    printf("%d\n",as);
    return 0;
}
View Code
上一篇:Search a 2D Matrix


下一篇:【MOBAN】树上莫队SP10707 COT2 - Count on a tree II