ZJOI 2010 基站选址 DP+SegmentTree

题目链接洛谷点我:-) bzoj点我:-)
题目描述
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。

输入格式
输入文件的第一行包含两个整数N,K,含义如上所述。
第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。
第三行包含N个整数,表示C1,C2,…CN。
第四行包含N个整数,表示S1,S2,…,SN。
第五行包含N个整数,表示W1,W2,…,WN。

输出格式
输出文件中仅包含一个整数,表示最小的总费用。

说明
40%的数据 N<=500;
100%的数据 K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。

思路
一.
应该不难想到DP, f[i][j]表示第i个村庄上建第j个基站,不考虑i后面的村庄,所需最小费用
转移: f[i][j] = f[k][j-1] + solve(i与k之间没有覆盖的村庄所需费用)(1<=k< i)
效率:O( n2k ),40分做法

二.
考虑优化,难点在于solve的计算。可以发现,对于每一个在村庄i前的村庄k,考虑i对它的影响,随着i的增大,它的右端点可能不可达到i。所以当达不到i时,考虑前面的村庄对它的影响,当i的前一个建基站的村庄的位置在k的左端点左方时,这个村庄不能被覆盖,需加上W[k]。
因此想到用线段树维护,每次对于一个i,枚举每一个以i为右端点的t,用区间加法在线段树中把t的左端点左方的村庄加上w[t]
效率:O( nlog(n)k ),100分做法
其他细节及实现见代码

感想
感觉自己已把生命托付给此题。。。T_T
昨天晚上看了题然后想啊想想了一个多小时,想到了DP方程,然后便卡死了
回家做了一点语文作业,然后再看题发现自己智障,题看错了。。。
然后又想,打了一点点代码,觉得这题不难,明天上午快点打完
之后又打了一上午,各种弱菜错误调了半个下午,啊,样例对了
之后死活WA啊 ,WA啊, WA啊
最后搞到了第一个点的数据,并且数据巨大,身无可恋の我慢慢改小数据,和标程对拍,终于发现了调了2个小时的错误:线段树中的某个qr打成了ql。。。终生难忘啊。。。
再也不能干上述智障的事了。。。

PS
然后给大家分享一下我看错的题目,同样数据范围,有人会做吗?
”就是把原题中的每一个村庄的覆盖条件改为它自己存在于某一个(或几个)基站的范围内“
(好像DP方程没什么问题,但后面的事儿我不会了-_-)

代码

//miaomiao 2017.1.24
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

#define lc (o<<1)
#define rc (o<<1|1)
#define pb push_back
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)

#define MAXK (100+5)
#define MAXN (20000+5)
#define INF 0x3f3f3f3f

void read(int &x){
    char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    x = 0;
    while(ch >= '0' && ch <= '9'){
        x = x*10+ch-'0';
        ch = getchar();
    }
}

int D[MAXN], C[MAXN], S[MAXN], W[MAXN], lr[MAXN], rr[MAXN], f[MAXN];
vector<int> vq[MAXN];

int ql, qr;

struct Seg_tree{
    int Min[MAXN*4], add[MAXN*4];

    void build(int o, int L, int R){
        if(L == R){
            add[o] = 0; Min[o] = f[L]; return;
        }

        int mid = (L+R)>>1;
        build(lc, L, mid); build(rc, mid+1, R);
        add[o] = 0; Min[o] = min(Min[lc], Min[rc]);
    }

    void maintain(int o, int L, int R){
        if(L!=R) Min[o] = min(Min[lc], Min[rc]); Min[o] += add[o];
    }

    void update(int o, int L, int R, int v){
        if(ql <= L && qr >= R) add[o] += v;
        else{
            int mid = (L+R)>>1;
            if(ql <= mid) update(lc, L, mid, v);
            if(qr > mid) update(rc, mid+1, R, v);  
           }
        maintain(o, L, R);
    }

    int query(int o, int L, int R, int Add){
        if(ql <= L && qr >= R) return Min[o]+Add;

        int ret = INF, mid = (L+R)>>1;
        if(ql <= mid) ret = min(ret, query(lc, L, mid, Add+add[o]));
        if(qr > mid) ret = min(ret, query(rc, mid+1, R, Add+add[o]));
        return ret;
    }
}ST;

int main(){
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif

    int n, k;
    read(n); read(k);
    For(i, 2, n) read(D[i]); For(i, 1, n) read(C[i]);
    For(i, 1, n) read(S[i]); For(i, 1, n) read(W[i]); 

    n++; k++;
    D[n] = INF;

    For(i, 1, n){
        lr[i] = lower_bound(D+1, D+n+1, D[i]-S[i])-D;
        rr[i] = lower_bound(D+1, D+n+1, D[i]+S[i])-D;
        if(D[i]+S[i] < D[rr[i]]) rr[i]--;

        vq[rr[i]].pb(i);
    }

    int ans = INF;
    For(j, 1, k){
        if(j == 1){
            int tot = 0;
            For(i, 1, n){
                f[i] = tot+C[i];
                For(tmp, 0, vq[i].size()-1) tot += W[vq[i][tmp]];
            }
            ans = min(ans, f[n]);
            continue;
        }

        ST.build(1, 1, n);
        For(i, 1, n){
            ql = 1; qr = i-1;
            int add = qr? ST.query(1, 1, n, 0): 0;
            f[i] = add+C[i];

            For(tmp, 0, vq[i].size()-1){
                ql = 1; qr = lr[vq[i][tmp]]-1;
                if(qr > 0) ST.update(1, 1, n, W[vq[i][tmp]]);
            }
        }
        ans = min(ans, f[n]);
    }
    printf("%d\n", ans);

    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值