CF #587 Div.3 F. Wi-Fi //线段树上dp+贪心

题目

题意

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] [ik,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] [ik,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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值