USACO 201901 白金組T1 redistricting題解

傳送門

題目大意

Bovinopolis城市有1n3×1051\le n\le3\times10^5塊牧草地形成一條線,每一塊有一頭牛,是Guernsey或Holstein(兩種牛的品種)。

現在要把這座城市的牧草地分爲一些連續的區間,每個區間有不超過1kn1\le k\le n個牧草地。

由於政府被Holstein控制,所以需要使Guernsey更多或Guernsey和Holsteins一樣多的區間個數最小化。請求出這個最小值。

題解

首先要學會O(nk)O(nk)的DP:設dp[i]dp[i]是前ii塊牧草地的答案。

把Holsteins標爲1,把Guernsey標爲-1,設前綴和爲S[i]S[i],則區間[l,r][l,r]被Guernsey佔領的充要條件是S[r]S[l1]0S[r]-S[l-1]\le0

dp[i]=maxj=i1ikdp[j]+[S[i]S[j]0] dp[i]=\max_{j=i-1}^{i-k}dp[j]+[S[i]-S[j]\le0]

dp[i]=maxj=i1ikdp[j]+[S[i]S[j]] dp[i]=\max_{j=i-1}^{i-k}dp[j]+[S[i]\le S[j]]

接下來考慮用線段樹優化。

對於每個1in1\le i\le n,分別統計線段樹下標(,S[i])(-\infin,S[i])[S[i],+)[S[i],+\infin)處的dp最小值,然後把dp[i]dp[i]存入S[i]S[i]的下標處。

記得實時滑動窗口使得線段樹存着dp[ik,,i1]dp[i-k,\dots,i-1]的信息。

最終dp[n]dp[n]就是答案。

那麼問題來了:如果兩個點的SS值相等,那麼會導致覆蓋信息,怎麼辦?

很簡單,把[1,n][1,n]中的點按SS值排序,並把nn個點按照這個順序存在線段樹裏即可。

#include<bits/stdc++.h>
using namespace std;
const int N=300005;
int n,k,s[N],tre[N<<2],p1[N],p2[N],d[N];//p1=value,p2=index
pair<int,int>p[N];
char str[N];
#define mid ((l+r)>>1)
#define ls c<<1,l,mid
#define rs c<<1|1,mid+1,r
void build(int c,int l,int r){
	tre[c]=N;if(l==r)return;
	build(ls),build(rs);
}
int query(int c,int l,int r,int L,int R){
	if(r<L||R<L||R<l)return N;
	if(L<=l&&r<=R)return tre[c];
	return min(query(ls,L,R),query(rs,L,R));
}
void upd(int c,int l,int r,int x,int v){
	if(l==r){tre[c]=v;return;}
	if(x<=mid)upd(ls,x,v);else upd(rs,x,v);
	tre[c]=min(tre[c<<1],tre[c<<1|1]);
}
int main(){
	freopen("redistricting.in","r",stdin);
	freopen("redistricting.out","w",stdout);
	scanf("%d%d%s",&n,&k,str+1);
	for(int i=1;i<=n;i++){s[i]=s[i-1];if(str[i]=='G')--s[i];else ++s[i];}
	for(int i=1;i<=n;i++)p[i]=make_pair(s[i],i);sort(p,p+n+1);
	for(int i=0;i<=n;i++)p1[i]=p[i].first,p2[p[i].second]=i;
	build(1,0,n);upd(1,0,n,p2[0],0);
	for(int i=1;i<=n;i++){
		int id=lower_bound(p1,p1+n+1,s[i])-p1;
		d[i]=min(query(1,0,n,0,id-1),query(1,0,n,id,n)+1);
		upd(1,0,n,p2[i],d[i]);if(k<=i)upd(1,0,n,p2[i-k],N);
	}
	printf("%d",d[n]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章