USACO 201901 白金组T1 redistricting题解

传送门

题目大意

Bovinopolis城市有 1 ≤ n ≤ 3 × 1 0 5 1\le n\le3\times10^5 1n3×105块牧草地形成一条线,每一块有一头牛,是Guernsey或Holstein(两种牛的品种)。

现在要把这座城市的牧草地分为一些连续的区间,每个区间有不超过 1 ≤ k ≤ n 1\le k\le n 1kn个牧草地。

由于政府被Holstein控制,所以需要使Guernsey更多或Guernsey和Holsteins一样多的区间个数最小化。请求出这个最小值。

题解

首先要学会 O ( n k ) O(nk) O(nk)的DP:设 d p [ i ] dp[i] dp[i]是前 i i i块牧草地的答案。

把Holsteins标为1,把Guernsey标为-1,设前缀和为 S [ i ] S[i] S[i],则区间 [ l , r ] [l,r] [l,r]被Guernsey占领的充要条件是 S [ r ] − S [ l − 1 ] ≤ 0 S[r]-S[l-1]\le0 S[r]S[l1]0

d p [ i ] = max ⁡ j = i − 1 i − k d p [ j ] + [ S [ i ] − S [ j ] ≤ 0 ] dp[i]=\max_{j=i-1}^{i-k}dp[j]+[S[i]-S[j]\le0] dp[i]=j=i1maxikdp[j]+[S[i]S[j]0]

d p [ i ] = max ⁡ j = i − 1 i − k d p [ j ] + [ S [ i ] ≤ S [ j ] ] dp[i]=\max_{j=i-1}^{i-k}dp[j]+[S[i]\le S[j]] dp[i]=j=i1maxikdp[j]+[S[i]S[j]]

接下来考虑用线段树优化。

对于每个 1 ≤ i ≤ n 1\le i\le n 1in,分别统计线段树下标 ( − ∞ , S [ i ] ) (-\infin,S[i]) (,S[i]) [ S [ i ] , + ∞ ) [S[i],+\infin) [S[i],+)处的dp最小值,然后把 d p [ i ] dp[i] dp[i]存入 S [ i ] S[i] S[i]的下标处。

记得实时滑动窗口使得线段树存着 d p [ i − k , … , i − 1 ] dp[i-k,\dots,i-1] dp[ik,,i1]的信息。

最终 d p [ n ] dp[n] dp[n]就是答案。

那么问题来了:如果两个点的 S S S值相等,那么会导致覆盖信息,怎么办?

很简单,把 [ 1 , n ] [1,n] [1,n]中的点按 S S S值排序,并把 n n n个点按照这个顺序存在线段树里即可。

#include
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;
}

你可能感兴趣的:(USACO,题解)