zoukankan      html  css  js  c++  java
  • P2605 [ZJOI2010]基站选址

    P2605 [ZJOI2010]基站选址

    浪费了洛谷28发提交,有必要写篇题解

    首先这个是dp无疑了。

    考虑设状态。最容易想到的应该是: (dp[i][j]) 表示考虑到第 (i) 个位置,建 (j) 个基站的最小代价,答案是 (min{dp[n][i]}(0le i le k))

    但是发现不好转移,那么换成:(dp[i][j]) 表示考虑到第 (i) 个位置,钦定第 (i) 个位置建基站,前 (i) 个位置总共建了 (j) 个基站的最小代价。

    这样在统计答案的时候要改成 (dp[i][j]+f(i,n)(1le i le n,0le j le k))(f(l,r)) 表示在 (l) 建基站,([l,r]) 中间总共需要赔偿多少。

    那么转移很显然了,(dp[i][j]=min{dp[k][j]+g(k,i) } (0le j<i)) ,(g(l,r)) 表示在 (l,r) 都建基站,([l,r]) 中间总共需要赔偿多少。

    边界:(dp[0][0]=0)

    (O(n^2k)) 解决了。

    然后我发现我只有 (20) 分。说好 (40\%) 的数据 (nle 500) 呢?数组开到 (1000) 就过了 #4(30) 分。

    为啥不是 (40) 啊,#2 怎么 WA 了啊?!然后陷入了困境,开始疯狂提交

    忽然发现,边界应该是 (dp[1][i]=h(1,i)),(h(l,r)) 表示在 (r) 建基站 ([l,r]) 中间总共需要赔偿多少。

    因为如果第一维从 (1) 开始转移的话,我们上面转移方程默认的是 (j) 左边覆盖不到的 (i) 一定覆盖不到,所以只用考虑 ([j,i]) 之间产生的贡献即可。但是一开始没有基站,所以这个条件不成立,然后就挂了。那怎么还有30分

    现在就有 (40) 了。

    后记:后面优化 (dp) 的时候拿暴力对拍发现 (40) 分的程序统计答案错了,但是它有 (40) 分。。。

    接下去考虑优化 (dp)

    (f,h) 两个函数都可以二分预处理出来(或者你看了后面的处理可以不用二分)。

    这个转移方程长得是一个区间最小值的形式,就是 (g(l,r)) 特别不爽,没法优化。

    这种时候往往考虑直接拆 (g) 函数,拆成最本质的形式。

    我们发现一段区间 ([D[k]-S[k],D[k]+S[k]]) 不包括 (i,j) 时 ,(g(i,j)+=W[k])

    对于一个确定的左端点 (j) ,它所能产生的贡献时确定的,主要是右端点 (i) 在不断右移,而不断右移就是一个 单调 的东西。

    这意味着一旦 (i) 往右移动到某个临界点,某一些 (k) 也无法被 (j) 覆盖的时候,(W[k]) 的贡献必然产生,而且这个贡献是针对某一段 (j) 产生的。

    到现在,线段树很明显了吧!

    首先二分预处理每一个位置 (i) ,能覆盖它的最靠左的端点 (L[i]) 和最靠右的端点 (R[i])

    对于每一个位置开 vector ,记录 (R[j]=i) 的区间编号 (id)

    每次从左往右扫,先查询 ([1,i-1]) 的最小值更新 (dp) 值,再把 ([1,L[id]-1]) 里的贡献加上 (W[id])

    其实 (dp) 数组可以开一维,迭代跑 (k) 次即可。

    空间 (O(n)) ,时间复杂度 (O(nklog n))

    //Orz cyn2006
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef double db;
    #define mkp(x,y) make_pair(x,y)
    #define fi first
    #define se second
    #define pb(x) push_back(x)
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return f?x:-x;
    }
    #define N 20005
    #define K 105
    #define T (N<<2)
    #define inf 1000000005
    int n,k,D[N],C[N],S[N],W[N],dp[N],ans,L[N],R[N],pre[N],suf[N];
    vector<pair<int,int> >v1[N],v2[N];
    int val[T],tag[T];
    #define lc (p<<1)
    #define rc (p<<1|1)
    void pushup(int p){val[p]=min(val[lc],val[rc]);}
    void build(int l=1,int r=n,int p=1){
    	tag[p]=0;
    	if(l==r)return val[p]=min(dp[l],inf),void();
    	int mid=(l+r)>>1;
    	build(l,mid,lc),build(mid+1,r,rc);
    	pushup(p);
    }
    void pushdown(int p){
    	if(tag[p]){
    		val[lc]+=tag[p],val[rc]+=tag[p];
    		tag[lc]+=tag[p],tag[rc]+=tag[p];
    		tag[p]=0;
    	}
    }
    void update(int ql,int qr,int k,int l=1,int r=n,int p=1){
    	if(ql>qr)return;
    	if(ql<=l&&r<=qr)return val[p]+=k,tag[p]+=k,void();
    	pushdown(p);
    	int mid=(l+r)>>1;
    	if(ql<=mid)update(ql,qr,k,l,mid,lc);
    	if(mid<qr)update(ql,qr,k,mid+1,r,rc);
    	pushup(p);
    }
    int query(int ql,int qr,int l=1,int r=n,int p=1){
    	if(ql>qr)return inf;
    	if(ql<=l&&r<=qr)return val[p];
    	pushdown(p);
    	int mid=(l+r)>>1;
    	if(qr<=mid)return query(ql,qr,l,mid,lc);
    	if(mid<ql)return query(ql,qr,mid+1,r,rc);
    	return min(query(ql,qr,l,mid,lc),query(ql,qr,mid+1,r,rc));
    }
    signed main(){
    	n=read(),k=read();
    	for(int i=2;i<=n;++i)D[i]=read();
    	for(int i=1;i<=n;++i)C[i]=read();
    	for(int i=1;i<=n;++i)S[i]=read();
    	for(int i=1;i<=n;++i)W[i]=read();
    	for(int i=1;i<=n;++i){
    		L[i]=lower_bound(D+1,D+n+1,D[i]-S[i])-D;
    		R[i]=upper_bound(D+1,D+n+1,D[i]+S[i])-D-1;
    		v1[R[i]].pb(mkp(L[i],i)),v2[L[i]].pb(mkp(R[i],i));
    	}
    	for(int i=1;i<=n;++i){
    		pre[i]+=pre[i-1];
    		for(int j=0,sz=v1[i].size();j<sz;++j)pre[i+1]+=W[v1[i][j].se];
    	}
    	for(int i=n;i>=1;--i){
    		suf[i]+=suf[i+1];
    		for(int j=0,sz=v2[i].size();j<sz;++j)suf[i-1]+=W[v2[i][j].se];
    	}
    	memset(dp,0x3f,sizeof(dp));
    	for(int i=1;i<=n;++i)ans+=W[i];
    	if(!k)return printf("%d
    ",ans),0;
    	for(int i=1;i<=n;++i)dp[i]=pre[i]+C[i],ans=min(ans,dp[i]+suf[i]);
    	for(int t=2;t<=k;++t){
    		build();
    		for(int i=1;i<=n;++i){
    			dp[i]=query(1,i-1)+C[i],ans=min(ans,dp[i]+suf[i]);
    			for(int j=0,sz=v1[i].size();j<sz;++j)update(1,v1[i][j].fi-1,W[v1[i][j].se]);
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    c#和unity引擎的所有笔记
    C#笔记(十九)——委托和事件
    委托
    c#笔记(十七)——队列
    js正则表达式
    mysql分页
    springMVC
    hibernate与spring整合实现transaction
    spring aop
    about hibernate lazy load and solution
  • 原文地址:https://www.cnblogs.com/zzctommy/p/13929926.html
Copyright © 2011-2022 走看看