• bzoj 1835: [ZJOI2010]base 基站选址


    Description

    有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。

    Input

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

    Output

    3 2 1 2 2 3 2 1 1 0 10 20 30

    Sample Input

    4

    Sample Output

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

    HINT

    Source

    Day1

    考虑转移方程,我们滚掉了第一维k:

    其中cost(j+1,i)表示[j+1,i]之间不能覆盖的点的∑wi;

    这样直接转移是n^3*k的,不行。。。

    我们发现cost(j+1,i)特别不好算,我们需要改变计算方法;

    我们记L[x]表示标号最小的能覆盖x的位置,R[x]表示标号最大的能覆盖x的位置;

    我们只要在[L[x],R[x]]之间修基站,都不会付出w[x]的代价,但是一旦当前枚举的基站点i跨过R[x],从[1,L[x]-1]开始转移的dp值都需要加上w[x]的代价;

    那么我们相当于进行一次区间加法操作,在每个右端点开一个vector,然后区间修改,zzd的某个联赛模拟题和这个计算代价的思想很像;

    具体实现的话就是分k次更新,每次重新build;

    注意末尾增加一个d[n+1]=Inf,w[n+1]=Inf的点,然后把k+1,可以免去末尾没有覆盖完的一系列特判;

    //MADE BY QT666
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    typedef long long ll;
    const int N=100050;
    const ll Inf=1926081719260817;
    struct data{
        int l,r,w;
    }g[N];
    vector<data> p[N];
    ll tr[N*4],lazy[N*4],dp[N],w[N],c[N],d[N],s[N];
    int n,k;
    void build(int x,int l,int r){
        lazy[x]=0;
        if(l==r){tr[x]=dp[l];return;}
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        tr[x]=min(tr[lson],tr[rson]);
    }
    void update(int x,int l,int r,int xl,int xr,int v){
        if(xl>xr) return;
        if(xl<=l&&r<=xr){
    	tr[x]+=v;lazy[x]+=v;return;
        }
        int mid=(l+r)>>1;
        if(xr<=mid) update(lson,l,mid,xl,xr,v);
        else if(xl>mid) update(rson,mid+1,r,xl,xr,v);
        else update(lson,l,mid,xl,mid,v),update(rson,mid+1,r,mid+1,xr,v);
        tr[x]=min(tr[lson],tr[rson])+lazy[x];
    }
    ll query(int x,int l,int r,int xl,int xr,int la){
        if(xl<=l&&xr<=r) return tr[x]+la;
        int mid=(l+r)>>1;la+=lazy[x];
        if(xr<=mid) return query(lson,l,mid,xl,xr,la);
        else if(xl>mid) return query(rson,mid+1,r,xl,xr,la);
        else return min(query(lson,l,mid,xl,mid,la),query(rson,mid+1,r,mid+1,xr,la));
    }
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=2;i<=n;i++) scanf("%d",&d[i]);
        for(int i=1;i<=n;i++) scanf("%d",&c[i]);
        for(int i=1;i<=n;i++) scanf("%d",&s[i]);
        for(int i=1;i<=n;i++) scanf("%d",&g[i].w);
        ++n;d[n]=Inf,w[n]=Inf;s[n]=0;c[n]=0;
        for(int i=1;i<=n;i++){
    	g[i].l=lower_bound(d+1,d+1+n,d[i]-s[i])-d;
    	g[i].r=lower_bound(d+1,d+1+n,d[i]+s[i])-d;
    	if(d[g[i].r]>d[i]+s[i]) g[i].r--;
    	p[g[i].r].push_back((data){g[i].l,g[i].r,g[i].w});
        }
        ll t=0;
        for(int i=1;i<=n;i++){
    	dp[i]=t+c[i];
    	for(int j=0;j<p[i].size();j++) t+=p[i][j].w;
        }
        ll ans=Inf;
        ans=min(ans,dp[n]);
        memset(tr,127,sizeof(tr));
        for(int j=2;j<=k+1;j++){
    	build(1,1,n);
    	for(int i=1;i<=n;i++){
    	    dp[i]=query(1,1,n,1,i-1,0)+c[i];
    	    for(int j=0;j<p[i].size();j++) update(1,1,n,1,p[i][j].l-1,p[i][j].w);
    	}
    	ans=min(ans,dp[n]);
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    python脚本2_输入2个数比较大小后从小到大升序打印
    python脚本1_给一个半径求圆的面积和周长
    配置双机互信
    如何在 CentOS7 中安装 Nodejs
    Git 服务器搭建
    docker安装脚本
    CentOS7下安装Docker-Compose
    Linux 文件锁
    6 系统数据文件和信息
    bash脚本编程之二 字符串测试及for循环
  • 原文地址:https://www.cnblogs.com/qt666/p/7659372.html
Copyright © 2020-2023  润新知