题目
题意
n ( 1 e 5 ) n(1e5) n(1e5)个房间相邻,单独第 i i i个房间装wifi需要 i i i花费。或者可以在可以装路由器的房间装路由器,第 i i i个房间装路由器,花费也是 i i i,但是 [ i − k , i + k ] [i-k,i+k] [i−k,i+k]的房间会有wifi,求最小的花费,使得每一个房间都有wifi信号。
思路
贪心是最难的算法。
定义
d
p
[
i
]
dp[i]
dp[i]:递推到第
i
i
i个房间时的总花费。
考虑,从右向左倒推,
i
i
i处装路由器wifi影响范围:
[
i
−
k
,
i
+
k
]
[i-k,i+k]
[i−k,i+k],每次走到有区间端点时候,去更新整个区间
d
p
dp
dp的答案。
暴力更新
d
p
dp
dp,复杂度爆炸,考虑线段树维护区间最小值(更新不是加,直接变成某个值。
每次递推时,考虑装路由器或者不装(装的话肯定要装靠左边的)
如果从左向右递推的话,不能尽量使得路由器向右啊!!!那么反过来递推就可行了!!!然后还要爆LL…
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MXN = 2e5+5;
const int MXLOG = 18;
int lz[MXN<<2],T[MXN<<2];
inline void pushUp(int rt){
T[rt]=min(T[rt<<1],T[rt<<1|1]);
}
void pushDown(int rt){
if(lz[rt]!=LLONG_MAX/10){
lz[rt<<1]=min(lz[rt],lz[rt<<1]);
lz[rt<<1|1]=min(lz[rt],lz[rt<<1|1]);
T[rt<<1]=min(lz[rt],T[rt<<1]);
T[rt<<1|1]=min(lz[rt],T[rt<<1|1]);
lz[rt]=LLONG_MAX/10;
}
}
void build(int l,int r,int rt){
lz[rt]=LLONG_MAX/10;
if(l==r){
T[rt]=LLONG_MAX/10;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
if(l>=L&&r<=R){
lz[rt]=min(lz[rt],c);
T[rt]=min(T[rt],c);
return;
}
pushDown(rt);
int m=(l+r)>>1;
if(m>=L) update(L,R,c,l,m,rt<<1);
if(m<R) update(L,R,c,m+1,r,rt<<1|1);
pushUp(rt);
}
int query(int L,int R,int l,int r,int rt){
if(l>=L&&r<=R)
return T[rt];
pushDown(rt);
int m=(l+r)>>1;
int re=LLONG_MAX/10;
if(m>=L) re=min(re,query(L,R,l,m,rt<<1));
if(m<R) re=min(re,query(L,R,m+1,r,rt<<1|1));
return re;
}
int to[MXN];
int midPos[MXN];
signed main(){
int n,k;cin>>n>>k;
string s;cin>>s;
build(1,n+1,1);
for(int i=1;i<=n;i++) to[i]=n+1;
for(int i=n-1;i>=0;i--){
if(s[i]=='1'){
int ll=max(1LL,i+1-k),rr=min(n,i+1+k);
if(ll<=to[rr]) to[rr]=ll,midPos[rr]=i+1;
}
}
update(n+1,n+1,0,1,n+1,1);
for(int i=n;i>=1;i--){
int t1=query(i,i,1,n+1,1),t2=query(i+1,i+1,1,n+1,1)+i;
update(i,i,min(t1,t2),1,n+1,1);
if(to[i]<i) update(to[i],i,midPos[i]+t2-i,1,n+1,1);
}
cout<<query(1,1,1,n+1,1);
return 0;
}