P5202 [USACO19JAN] Redistricting P
首先我们设更赛牛为加一,荷斯坦牛为负一。
这样通过前缀和就可以得到这一组是否需要增加一。
设 \(dp_i\) 表示以 \(i\) 为末尾,最少的分区。
那么方程式就为:
\[dp_i=dp_j+(pre_i-pre_j\le 0)
\]
然而表达式我们并不好判断。
但是由于表达式只能提供数值为一的贡献,那么我们可以使用优先队列,以 \(dp_j\) 的值排序,在 \(dp_j\) 相同时按照 \(pre_j\) 排序,取出最小值即可。
由于选择的区域有限,第一种处理方式是在结构体内记录下位置,若取出的值不合法则删除后再取一次。
对于第二种方法,可以直接使用带删的优先队列。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,dp[300005],pre[300005];
char a[300005];
struct P{
int x,y;
bool friend operator<(P a,P b){
if(a.x!=b.x)return a.x>b.x;
return a.y>b.y;
}
};
struct Q{
priority_queue<P> q1,q2;
void push(P x){
q1.push(x);
}
void pop(P x){
q2.push(x);
}
P top(){
while(!q1.empty()&&!q2.empty()&&q1.top().x==q2.top().x&&q1.top().y==q2.top().y){
q1.pop();
q2.pop();
}
return q1.top();
}
}q;
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
if(a[i]=='G')pre[i]=-1;
else pre[i]=1;
}
for(int i=1;i<=n;i++)pre[i]+=pre[i-1];
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
q.push({0,0});
for(int i=1;i<=n;i++){
if(i-k-1>=0)q.pop({dp[i-k-1],pre[i-k-1]});
P u=q.top();
dp[i]=u.x+(pre[i]-u.y<=0);
q.push({dp[i],pre[i]});
}
cout<<dp[n];
return 0;
}

浙公网安备 33010602011771号