bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=1835

【题意】

有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄建基站的费用为c[i],如果在距离村i不超过s[i]内有基站则该村被覆盖,村i不被覆盖的补偿费为w[i],求最少花费。

【思路】

设f[i][j]表示第i个村建第j个基站的最小花费,则有转移式:

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

cost(k,i)=sigma{ w[x] } k+1<=x<=i-1 , 且x未被覆盖

f[][]需要求一个区间最小值,我们尝试用线段树维护每一层的这个值。

枚举j,考虑每一层i。

我们设st[i],ed[i]分别表示在i左右距离i最远的st[i],ed[i]建基站依旧可以覆盖到i,假设我们已经求完了f[i][j]要求f[i+1][j],考虑那些恰可以被i覆盖到而不能被i+1覆盖到的,即满足ed[x]=i的点,将[1..st[x]-1]区间内的线段树值都加w[x]。求f[i]的时候查询区间[1..i-1]内线段树值的最小即可。

其中st[i],ed[i]可以用二分法求。

线段树提供区间操作区间查询的操作。

总的时间复杂度为O(nmlogn)

辣鸡线段树,毁我青春(连个线段树都不会写了T^T

【代码】

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 10 using namespace std;
 11
 12 typedef long long ll;
 13 const int N = 1e5+10;
 14 const int inf = 1e9;
 15
 16 ll read() {
 17     char c=getchar();
 18     ll f=1,x=0;
 19     while(!isdigit(c)) {
 20         if(c==‘-‘) f=-1; c=getchar();
 21     }
 22     while(isdigit(c))
 23         x=x*10+c-‘0‘,c=getchar();
 24     return x*f;
 25 }
 26
 27 int n,K; ll f[N];
 28 ll d[N],c[N],s[N],w[N],st[N],ed[N];
 29 vector<ll> ep[N];
 30
 31 struct Tnode {
 32     int l,r; ll v,tag;
 33 }T[N<<1];
 34
 35 void pushdown(int u)
 36 {
 37     if(T[u].l==T[u].r||(!T[u].tag)) return ;
 38     ll& t=T[u].tag;
 39     T[u<<1].v+=t,T[u<<1].tag+=t;
 40     T[u<<1|1].v+=t,T[u<<1|1].tag+=t;
 41     t=0;
 42 }
 43 void maintain(int u)
 44 {
 45     T[u].v=min(T[u<<1].v,T[u<<1|1].v);
 46 }
 47 void build(int u,int l,int r)
 48 {
 49     T[u].l=l,T[u].r=r;
 50     T[u].tag=0;
 51     if(l==r) T[u].v=f[l];
 52     else {
 53         int mid=l+r>>1;
 54         build(u<<1,l,mid);
 55         build(u<<1|1,mid+1,r);
 56         maintain(u);
 57     }
 58 }
 59 void Add(int u,int L,int R,ll x)
 60 {
 61     if(L>R) return ;                    //处理 L>R
 62     pushdown(u);
 63     if(L<=T[u].l&&T[u].r<=R)
 64         T[u].v+=x,T[u].tag+=x;
 65     else {
 66         int mid=T[u].l+T[u].r>>1;
 67         if(L<=mid) Add(u<<1,L,R,x);
 68         if(mid<R) Add(u<<1|1,L,R,x);
 69         maintain(u);
 70     }
 71 }
 72 ll query(int u,int L,int R)
 73 {
 74     if(L>R) return 0;
 75     pushdown(u);
 76     if(L<=T[u].l&&T[u].r<=R) return T[u].v;
 77     else {
 78         int mid=T[u].l+T[u].r>>1; ll ans=inf;
 79         if(L<=mid) ans=min(ans,query(u<<1,L,R));
 80         if(mid<R) ans=min(ans,query(u<<1|1,L,R));
 81         return ans;
 82     }
 83 }
 84
 85 //lower_bound定义为找到第一个不小于v的数的指针
 86 void init()
 87 {
 88     n=read(),K=read();
 89     FOR(i,2,n) d[i]=read();
 90     FOR(i,1,n) c[i]=read();
 91     FOR(i,1,n) s[i]=read();
 92     FOR(i,1,n) w[i]=read();
 93     n++,K++;
 94     d[n]=inf; w[n]=inf;
 95     FOR(i,1,n) {
 96         int l=d[i]-s[i],r=d[i]+s[i];
 97         l=lower_bound(d+1,d+n+1,l)-d;
 98         r=lower_bound(d+1,d+n+1,r)-d;
 99         if(d[i]+s[i]<d[r]) r--;
100         st[i]=l,ed[i]=r;
101         ep[ed[i]].push_back(i);
102     }
103 }
104 ll dp()
105 {
106     ll ans,tmp=0;
107     FOR(i,1,n) {
108         f[i]=tmp+c[i];
109         FOR(j,0,(int)ep[i].size()-1)
110             tmp+=w[ep[i][j]];
111     }
112     ans=f[n];
113     FOR(j,2,K) {
114         build(1,1,n);
115         FOR(i,1,n) {
116             f[i]=query(1,1,i-1)+c[i];
117             FOR(k,0,(int)ep[i].size()-1) {
118                 int x=ep[i][k];
119                 Add(1,1,st[x]-1,w[x]);
120             }
121         }
122         ans=min(ans,f[n]);
123     }
124     return ans;
125 }
126
127 int main()
128 {
129     //freopen("in.in","r",stdin);
130     //freopen("out.out","w",stdout);
131     init();
132     printf("%lld",dp());
133     return 0;
134 }
时间: 2024-10-11 13:11:51

bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)的相关文章

[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)

首先想到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而

BZOJ 1835 基站选址(DP+线段树)

# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

BZOJ 1835 基站选址(线段树优化DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1835 题意:有N个村庄坐落在一条直线上,第 i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村 庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位 置,使得总费用最小. 思路: 另外,程序中的n=n+1,m=

bzoj1835[ZJOI2010]base基站选址

据说正解是什么线段树优化DP,但是作为脑子有坑选手,我们需要5k的做法: 主席树+决策单调性..... F[m][i]表示已经放置了m个基站,第m个基站放置在第i个村庄,第i个村庄及之前的村庄的总最少花费(包括建立基站的花费和赔偿的花费),转移的时候,F[m][i]=min(F[m-1][j]+cost(j,i))+ci Cost(j,i)表示在点j和点i各建立一个基站,j和i之间不建立基站时,j和i之间需要的总赔偿. 考虑如何快速求出cost(j,i).对于每个村庄k我们可以在位置坐标轴上二分

[ZJOI2010]基站选址(线段树优化dp)

坑待填. \(Code\ Below:\) #include <bits/stdc++.h> #define lson (rt<<1) #define rson (rt<<1|1) using namespace std; const int maxn=20000+10; const int inf=0x3f3f3f3f; int n,k,d[maxn],c[maxn],s[maxn],w[maxn],f[maxn],st[maxn],ed[maxn],sum[maxn

题解 HDU 3698 Let the light guide us Dp + 线段树优化

http://acm.hdu.edu.cn/showproblem.php?pid=3698 Let the light guide us Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 62768/32768 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 253 Problem Description Plain of despair was

[后缀数组+dp/AC自动机+dp+线段树] hdu 4117 GRE Words

题意: 给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w.每个串有个值.这个值[-1000, 1000]. 问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值. 思路: 其实就是一个很明显的dp. dp[i]代表以第i个字符串结尾的最大权值. 但是就是子串这个问题怎么处理. 由于这题数据比较水可以用后缀数组处理这个问题. 将所有字符串拼接,做sa. 每次在height数组里往上和往下寻找公共前缀等

BZOJ 1852:[MexicoOI06]最长不下降序列(贪心+DP+线段树+离散化)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1852 [题目大意] 给你N对数A1,B1……An,Bn.要求你从中找出最多的对, 把它们按照一种方式排列,重新标号1,2,..,k.能满足对于每一对i<j,都有Ai>Bj. [题解] 对于排序的问题,如果i必须要在j前面, 那么有A[i]>B[j],且B[i]>=A[j],相加得A[i]+B[i]>A[j]+B[j], 因此按A+B从大到小排序后最优, 我们先将A