BZOJ 1835 [ZJOI2010]基站選址 (線段樹優化DP)
阿新 • • 發佈:2019-01-13
geo har while 位置 如何 lse problem space getchar
題目大意:略 洛谷題面傳送門 BZOJ題面傳送門
註意題目的描述,是村莊在一個範圍內去覆蓋基站,而不是基站覆蓋村莊,別理解錯了
定義$f[i][k]$表示只考慮前i個村莊,一共建了$k$個基站,最後一個基站建在了i處,最小的總花費
$f[i][k]=min(f[j][k]+calc(j,i))\;calc(j,i)$表示$i$和$j$之間,無法被覆蓋的點,需要付的補償總和
考慮如何求出$calc(j,i)$
定義$st_{i}$,$ed_{i}$表示第$i$個村莊能覆蓋的最左端點和最右端點
即$st_{i}$到$ed_{i}$之間只要有一個村莊有基站,那麽村莊i就不需要被補償
可以用二分查找實現
把相同$ed_{i}$的村莊編號記錄在$ed_{i}$這個位置
$DP$時,我們從左往右遍歷要建基站的位置$x$,如果有一個村莊$i$的$ed_{i}<$當前位置$x$,那麽如果$x$的決策如果選在了$[1,st_{i}-1]$,即上一個基站建在了$[1,st_{i}-1]$,那麽村莊$i$需要被補償,區間修改,用線段樹實現
而轉移就是查詢區間最小值,同樣用線段樹實現即可
細節略多
1 #include <vector> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5#define N1 20010 6 #define K1 105 7 #define ll long long 8 #define dd double 9 #define inf 0x3f3f3f3f3f3f3f3fll 10 using namespace std; 11 12 int gint() 13 { 14 int ret=0,fh=1;char c=getchar(); 15 while(c<‘0‘||c>‘9‘){if(c==‘-‘)fh=-1;c=getchar();} 16 while(c>=‘0‘&&c<=‘9‘){ret=ret*10+c-‘0‘;c=getchar();} 17 return ret*fh; 18 } 19 20 struct SEG{ 21 ll mi[N1<<2],tag[N1<<2]; 22 inline void pushup(int rt){ mi[rt]=min(mi[rt<<1],mi[rt<<1|1]); } 23 inline void pushdown(int rt) 24 { 25 if(!tag[rt]) return; 26 mi[rt<<1]+=tag[rt]; mi[rt<<1|1]+=tag[rt]; 27 tag[rt<<1]+=tag[rt]; tag[rt<<1|1]+=tag[rt]; 28 tag[rt]=0; 29 } 30 void build(ll *f,int l,int r,int rt) 31 { 32 tag[rt]=0; 33 if(l==r){ mi[rt]=f[l]; return; } 34 int mid=(l+r)>>1; 35 build(f,l,mid,rt<<1); 36 build(f,mid+1,r,rt<<1|1); 37 pushup(rt); 38 } 39 void update(int L,int R,int l,int r,int rt,ll w) 40 { 41 if(L<=l&&r<=R){ mi[rt]+=w; tag[rt]+=w; return; } 42 int mid=(l+r)>>1; pushdown(rt); 43 if(L<=mid) update(L,R,l,mid,rt<<1,w); 44 if(R>mid) update(L,R,mid+1,r,rt<<1|1,w); 45 pushup(rt); 46 } 47 ll query(int L,int R,int l,int r,int rt) 48 { 49 if(L<=l&&r<=R) return mi[rt]; 50 int mid=(l+r)>>1; ll ans=inf; pushdown(rt); 51 if(L<=mid) ans=min(ans,query(L,R,l,mid,rt<<1)); 52 if(R>mid) ans=min(ans,query(L,R,mid+1,r,rt<<1|1)); 53 return ans; 54 } 55 }s; 56 57 vector<int>id[N1]; 58 int n,K; 59 int d[N1],c[N1],p[N1],w[N1],st[N1],ed[N1]; 60 ll f[N1]; 61 62 int main() 63 { 64 scanf("%d%d",&n,&K); 65 int i,j,k,l,r,x,mid; ll ans=inf; 66 for(i=2;i<=n;i++) d[i]=gint(); 67 for(i=1;i<=n;i++) c[i]=gint(); 68 for(i=1;i<=n;i++) p[i]=gint(); 69 for(i=1;i<=n;i++) w[i]=gint(); 70 for(i=1;i<=n;i++) 71 { 72 l=1,r=i,st[i]=i; 73 while(l<=r) 74 { 75 mid=(l+r)>>1; 76 if(d[mid]>=d[i]-p[i]) st[i]=mid,r=mid-1; 77 else l=mid+1; 78 } 79 l=i,r=n,ed[i]=i; 80 while(l<=r) 81 { 82 mid=(l+r)>>1; 83 if(d[mid]<=d[i]+p[i]) ed[i]=mid,l=mid+1; 84 else r=mid-1; 85 } 86 id[ed[i]].push_back(i); 87 } 88 memset(s.mi,0x3f,sizeof(s.mi)); 89 s.update(0,0,0,n,1,-(inf)); 90 for(k=1;k<=K;k++) 91 { 92 for(i=1;i<=n+1;i++) 93 { 94 f[i]=s.query(0,i-1,0,n,1)+c[i]; 95 for(j=0;j<id[i].size();j++) 96 { 97 x=id[i][j]; 98 s.update(0,st[x]-1,0,n,1,w[x]); 99 } 100 } 101 ans=min(ans,f[n+1]); 102 s.build(f,0,n,1); 103 } 104 for(i=1;i<=n;i++) 105 { 106 for(j=0;j<id[i].size();j++) 107 { 108 x=id[i][j]; 109 s.update(0,st[x]-1,0,n,1,w[x]); 110 } 111 } 112 ans=min(ans,s.query(0,n,0,n,1)); 113 printf("%lld\n",ans); 114 return 0; 115 }
BZOJ 1835 [ZJOI2010]基站選址 (線段樹優化DP)