• P2605 [ZJOI2010]基站选址


    \(\text{Solution}\)

    一道比较简单线段树优化\(DP\),但它是黑色的!!!
    一个显然的\(DP\),设\(f_{i,j}\)表示选到第\(i\)个(\(i\)必选),建了\(j\)个基站。

    \[f_{i,j} = f_{k,j - 1} + Sw_{k,i} + C_i \]

    \(Sw_{i,j}\)表示\(i\)\(j\)中没被覆盖的村庄的补偿总费用
    这样\(DP\)\(O(n^2)\)
    发现对于一个村庄\(x\),如果转移的\(j\)不在区间\([x - S_x,x + S_x]\)内就会加\(W_x\),那么就可以用线段树维护\(DP\)值,对区间\([1,x - S_x - 1]\)的数加上\(W_x\)即可。
    最后的答案在随便处理一下即可。

    \(\text{Code}\)

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 2e4 + 5,inf = 1e9;
    int n,m,d[N],c[N],w[N],f[N << 2],tag[N << 2],g[N][105];
    
    struct nd{int l,r,id;}a[N];
    bool cmp(nd x,nd y){return x.r < y.r;}
    void build(int l,int r,int k,int x)
    {
    	tag[k] = 0;
    	if (l == r) return f[k] = g[l][x],void();
    	int mid = l + r >> 1;
    	build(l,mid,k << 1,x),build(mid + 1,r,k << 1 | 1,x);
    	f[k] = min(f[k << 1],f[k << 1 | 1]);
    }
    void pushdown(int k)
    {
    	if (!tag[k]) return;
    	tag[k << 1] += tag[k],tag[k << 1 | 1] += tag[k];
    	f[k << 1] += tag[k],f[k << 1 | 1] += tag[k],tag[k] = 0;
    }
    void update(int l,int r,int k,int L,int R,int v)
    {
    	if (L > R || l > R || r < L || !v) return;
    	if (L <= l && r <= R) return f[k] += v,tag[k] += v,void();
    	pushdown(k); int mid = l + r >> 1;
    	if (L <= mid) update(l,mid,k << 1,L,R,v);
    	if (R > mid) update(mid + 1,r,k << 1 | 1,L,R,v);
    	f[k] = min(f[k << 1],f[k << 1 | 1]);
    }
    int query(int l,int r,int k,int L,int R)
    {
    	if (L <= l && r <= R) return f[k];
    	pushdown(k); int mid = l + r >> 1,tmp = inf;
    	if (L <= mid) tmp = query(l,mid,k << 1,L,R);
    	if (R > mid) tmp = min(tmp,query(mid + 1,r,k << 1 | 1,L,R));
    	return tmp;
    }
    int main()
    {
    	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,s; i <= n; i++)
    	{
    		scanf("%d",&s),a[i] = nd{d[i] - s,d[i] + s,i};
    		int l = 1,r = n,res = 1;
    		while (l <= r) {
    			int mid = l + r >> 1;
    			if (d[mid] >= a[i].l) res = mid,r = mid - 1;
    			else l = mid + 1;
    		}a[i].l = res,l = 1,r = n,res = n; 
    		while (l <= r) {
    			int mid = l + r >> 1;
    			if (d[mid] <= a[i].r) res = mid,l = mid + 1;
    			else r = mid - 1;
    		}a[i].r = res;
    	}
    	for (int i = 1; i <= n; i++) scanf("%d",&w[i]);
    	n++,a[n] = nd{n,n,n}; 
    	sort(a + 1,a + 1 + n,cmp); int ans = inf;
    	for (int i = 0; i <= n; i++)
    		for (int j = 0; j <= m; j++) g[i][j] = inf; 
    	g[0][0] = 0;
    	for (int j = 1; j <= m + 1; j++)
    	{
    		build(0,n,1,j - 1);
    		for (int i = 1,k = 1; i <= n; i++)
    		{
    			while (k <= n && a[k].r < i) update(0,n,1,0,a[k].l - 1,w[a[k].id]),k++;
    			g[i][j] = query(0,n,1,0,i - 1) + c[i]; 
    		}
    	}
    	for (int i = 1; i <= m + 1; i++) ans = min(ans,g[n][i]);
    	printf("%d\n",ans);
    }
    
  • 相关阅读:
    技术文章应该怎么写?
    后退时保存表单状态
    [原]长表头表格 竖直仅滚动内容区 水平滚动表头和内容区
    IE7不经提示关闭浏览器窗口
    meta 标记
    demo : 简单的 xslt 递归解析 xml 成 tree
    使用iframe和table模拟frameset的resize功能.html
    一个下划线(_)引发的"疑难杂症"
    几点小东西
    使用 ActiveReports 的 subReport 几点疑惑
  • 原文地址:https://www.cnblogs.com/nibabadeboke/p/16014078.html
Copyright © 2020-2023  润新知