题目链接:洛谷点我:-) 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;
}