zoukankan      html  css  js  c++  java
  • 【洛谷P2605】基站选址

    题目

    题目链接:https://www.luogu.com.cn/problem/P2605
    (N) 个村庄坐落在一条直线上,第 (i(i>1)) 个村庄距离第 (1) 个村庄的距离为 (D_i)。需要在这些村庄中建立不超过 (K) 个通讯基站,在第 (i) 个村庄建立基站的费用为 (C_i)。如果在距离第 (i) 个村庄不超过 (S_i) 的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第 (i) 个村庄没有被覆盖,则需要向他们补偿,费用为 (W_i)。现在的问题是,选择基站的位置,使得总费用最小。
    (Nleq 20000,Kleq 100)

    思路

    (f[i][j]) 表示在前 (i) 个村庄,安装了 (j) 个通讯基站的最小代价。
    考虑枚举上一次选择安装基站的位置 (k),有转移

    [f[i][j]=min_{k<i}left(f[k][j-1]+sum^{i-1}_{l=k+1}[d_l+s_l<d_i] imes [d_l-s_l>d_k] imes 1 ight) ]

    直接转移是 (O(n^2k)) 的,看到区间最小值,考虑维护 (k) 棵线段树优化转移。
    对于一个位置 (k(k<i)),如果满足 (d_k+s_k<d_i),那么对于 (d_k-s_k>d_j) 的所有 (j),从 (f[j][l-1]) 转移到 (f[i][l]) 的时候都会算上 (w_k) 的贡献。所以我们可以按照 (d_i-s_i) 从小到大排序,当我们枚举到 (i) 时,维护一个指针 (j),把所有 (d_j+s_j<d_i)(j),在线段树上区间 ([1,d_j-s_j-1]) 加上 (w_j) 即可。
    时间复杂度 (O(nklog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=20010,M=101,Inf=2e9;
    int n,m,ans,sum,d[N],c[N],s[N],w[N],id[N],L[N],f[N][M];
    
    bool cmp1(int x,int y)
    {
    	return d[x]+s[x]<d[y]+s[y];
    }
    
    bool cmp2(int x,int y)
    {
    	return d[x]-s[x]>d[y]-s[y];
    }
    
    struct SegTree
    {
    	int lazy[N*4],minn[N*4];
    	
    	void pushdown(int x)
    	{
    		if (lazy[x])
    		{
    			minn[x*2]+=lazy[x]; minn[x*2+1]+=lazy[x];
    			lazy[x*2]+=lazy[x]; lazy[x*2+1]+=lazy[x];
    			lazy[x]=0;
    		}
    	}
    	
    	void update(int x,int l,int r,int ql,int qr,int v)
    	{
    		if (ql>qr) return;
    		if (ql<=l && qr>=r)
    			return (void)(lazy[x]+=v,minn[x]+=v);
    		pushdown(x);
    		int mid=(l+r)>>1;
    		if (ql<=mid) update(x*2,l,mid,ql,qr,v);
    		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,v);
    		minn[x]=min(minn[x*2],minn[x*2+1]);
    	}
    	
    	int query(int x,int l,int r,int ql,int qr)
    	{
    		if (ql>qr) return 0;
    		if (ql<=l && qr>=r) return minn[x];
    		pushdown(x);
    		int mid=(l+r)>>1,res=Inf;
    		if (ql<=mid) res=min(res,query(x*2,l,mid,ql,qr));
    		if (qr>mid) res=min(res,query(x*2+1,mid+1,r,ql,qr));
    		return res;
    	}
    }seg[M];
    
    int main()
    {
    	cerr<<(sizeof(seg)+sizeof(f))/1024/1024<<"
    ";
    	scanf("%d%d",&n,&m);
    	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",&w[i]);
    	for (int i=1;i<=n;i++)
    	{
    		id[i]=i; ans+=w[i];
    		L[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d-1;
    	}
    	sort(id+1,id+1+n,cmp1);
    	for (int i=1,k=1;i<=n;i++)
    	{
    		for (;k<=n && d[id[k]]+s[id[k]]<d[i];k++)
    		{
    			sum+=w[id[k]];
    			for (int j=0;j<=m;j++)
    				seg[j].update(1,1,n,1,L[id[k]],w[id[k]]);
    		}
    		f[i][1]=c[i]+sum;
    		seg[1].update(1,1,n,i,i,f[i][1]);
    		for (int j=2;j<=m;j++)
    		{
    			f[i][j]=seg[j-1].query(1,1,n,1,i-1)+c[i];
    			seg[j].update(1,1,n,i,i,f[i][j]);
    		}
    	}
    	sum=0;
    	sort(id+1,id+1+n,cmp2);
    	for (int i=n;i>=1;i--) w[i]+=w[i+1];
    	for (int i=n,k=1;i>=1;i--)
    	{
    		for (;k<=n && d[id[k]]-s[id[k]]>d[i];k++)
    			sum+=w[id[k]]-w[id[k]+1];
    		for (int j=1;j<=m;j++)
    			ans=min(ans,f[i][j]+sum);
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    Qt5:快速设计对话框 QtDesigner 的使用
    Qt5:为菜单项添加图标 、 快捷键 和 状态栏 提示
    Qt5:在窗口上创建菜单栏
    Qt5:主窗口的创建 子类化QMainWindow
    AltiumDesigner设计快速入门
    AT24C02的MSP430驱动
    DSP_Builder设计方法说明_SinWave
    矩阵按键的MSP430驱动函数
    DAC8552使用说明
    PS2的FPGA解码模块
  • 原文地址:https://www.cnblogs.com/stoorz/p/15114151.html
Copyright © 2011-2022 走看看