首先想到DP,f[i][j]表示前i个村庄,共建了j个站的最小费用,且第j个站建在第i个村庄上

f[i][j]=min(f[i][j],f[k][j-1]+cost(k,i));(1<=k<i)

cost(k,i)表示选了k和i之后,他们之间需要的w的和

然后这样是O(kn^2)的,对于100%的数据会T。我们可以发现瓶颈在于找到min(f[k][j-1]+cost(k,i)),考虑如何优化它。还有显然的是可以舍掉第二维,只要先枚举建的站的数量即可。

当i变为i+1时,对于那些原来能建立了i而被覆盖,现在却不能被覆盖的点,就需要k来覆盖它。我们定义L[i],R[i],分别表示i最左边能覆盖i的点,和最右边的。于是每次对于R[x]=i的点,1~L[x]-1的点的cost都要加上w[x]。于是我们可以用一个线段树维护min(f[k][j-1]+cost(k,i)),求L[i]和R[i]二分就好。

注意一个细节,因为我们并不知道最后一个站建在哪,所以可以手动加一个点,令他的w=inf,c=0,d=inf,s=0,这样就一定会选它了。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<string>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#include<vector>
typedef long long LL;
using namespace std;
const int N=200010,inf=1e9;
struct node{int l,r,v,lazy;}a[N<<4];
int n,m,d[N],w[N],s[N],c[N],f[N],L[N],R[N];
vector<int>vec[N];
inline int read() {int d=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') d=(d<<3)+(d<<1)+c-48,c=getchar(); return d*f;}
void judge(){freopen(".in","r",stdin); freopen(".out","w",stdout);}
inline void pushup(int k){a[k].v=min(a[k<<1].v,a[k<<1|1].v);}
inline void pushdown(int k)
{
    if (!a[k].lazy) return;
    int k1=k<<1,k2=k1|1;
    a[k1].lazy+=a[k].lazy; a[k2].lazy+=a[k].lazy;
    a[k1].v+=a[k].lazy; a[k2].v+=a[k].lazy;
    a[k].lazy=0;
}
inline void build(int k,int l,int r)
{
    a[k]=(node){l,r,0,0};
    if (l==r) {a[k].v=f[l]; return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid); build(k<<1|1,mid+1,r);
    pushup(k);
}
inline void update(int k,int l,int r,int v)
{
    if (l>r) return;
    if (l<=a[k].l&&a[k].r<=r) {a[k].v+=v; a[k].lazy+=v; return;}
    pushdown(k);
    int mid=(a[k].l+a[k].r)>>1;
    if (r<=mid) update(k<<1,l,r,v);
    else if (l>mid) update(k<<1|1,l,r,v);
    else update(k<<1,l,mid,v),update(k<<1|1,mid+1,r,v);
    pushup(k);
}
inline int query(int k,int l,int r)
{
    if (l>r) return 0;
    if (l<=a[k].l&&a[k].r<=r) return a[k].v;
    pushdown(k);
    int mid=(a[k].l+a[k].r)>>1;
    if (r<=mid) return query(k<<1,l,r);
    else if (l>mid) return query(k<<1|1,l,r);
    else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
int main()
{
    //judge();
    n=read(); m=read();
    for (int i=2;i<=n;i++) d[i]=read();
    for (int i=1;i<=n;i++) c[i]=read();
    for (int i=1;i<=n;i++) s[i]=read();
    for (int i=1;i<=n;i++) w[i]=read();
    n++; d[n]=inf; c[n]=0; s[n]=0; w[n]=inf;
    for (int i=1;i<=n;i++)
    {
        L[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d;
        R[i]=lower_bound(d+1,d+1+n,d[i]+s[i])-d;
        if (d[R[i]]>d[i]+s[i]) R[i]--;
        vec[R[i]].push_back(i);
    }
    int ljj=0;
    for (int i=1;i<=n;i++)
    {
        f[i]=ljj+c[i];
        for (int j=0;j<vec[i].size();j++) ljj+=w[vec[i][j]];
    }
    int ans=f[n];
    for (ljj=1;ljj<=m;ljj++)
    {
        build(1,1,n);
        for (int i=1;i<=n;i++)
        {
            f[i]=query(1,1,i-1)+c[i];
            for (int j=0;j<vec[i].size();j++)
            {
                int k=vec[i][j];
                update(1,1,L[k]-1,w[k]);
            }
        }
        ans=min(ans,f[n]);
    }
    printf("%d",ans);
    return 0;
}
View Code

话说stl是真方便!

注:ZJOI2009的题好鬼畜啊,不想做了,07,08的不写题解了,可以看小乐乐的博客:http://blog.163.com/eden_284914869/