传送门
Bovinopolis城市有 1 ≤ n ≤ 3 × 1 0 5 1\le n\le3\times10^5 1≤n≤3×105块牧草地形成一条线,每一块有一头牛,是Guernsey或Holstein(两种牛的品种)。
现在要把这座城市的牧草地分为一些连续的区间,每个区间有不超过 1 ≤ k ≤ n 1\le k\le n 1≤k≤n个牧草地。
由于政府被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[l−1]≤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=i−1maxi−kdp[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=i−1maxi−kdp[j]+[S[i]≤S[j]]
接下来考虑用线段树优化。
对于每个 1 ≤ i ≤ n 1\le i\le n 1≤i≤n,分别统计线段树下标 ( − ∞ , 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[i−k,…,i−1]的信息。
最终 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;
}