題目大意
Bovinopolis城市有塊牧草地形成一條線,每一塊有一頭牛,是Guernsey或Holstein(兩種牛的品種)。
現在要把這座城市的牧草地分爲一些連續的區間,每個區間有不超過個牧草地。
由於政府被Holstein控制,所以需要使Guernsey更多或Guernsey和Holsteins一樣多的區間個數最小化。請求出這個最小值。
題解
首先要學會的DP:設是前塊牧草地的答案。
把Holsteins標爲1,把Guernsey標爲-1,設前綴和爲,則區間被Guernsey佔領的充要條件是。
即
接下來考慮用線段樹優化。
對於每個,分別統計線段樹下標和處的dp最小值,然後把存入的下標處。
記得實時滑動窗口使得線段樹存着的信息。
最終就是答案。
那麼問題來了:如果兩個點的值相等,那麼會導致覆蓋信息,怎麼辦?
很簡單,把中的點按值排序,並把個點按照這個順序存在線段樹裏即可。
#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;
}