题目大意
奶牛们的特大城市,牛都,要进行重新分区了!——这总是一个在居住在这里的两大主要种族(荷斯坦牛和更赛牛)之间富有争议的政治事件,因为两大种族都想要在牛都*中保持足够的影响力。 牛都的大都市圈由一列N块牧草地(1≤N≤3*1e5)组成,每块里有一头奶牛,均为荷斯坦牛和更赛牛之一。
牛都*想要将大都市圈划分为若干个连续的区,使得每个区至多包含K块牧草地(1≤K≤N),并且每块牧草地恰好属于一个区。由于*当前由荷斯坦牛控制,她们想要找到一种分区方式能够最小化更赛牛较多或者均势的区的数量(如果更赛牛的数量与荷斯坦牛的数量相等那么这个区就是均势的)。
有一个关心政治的更赛牛团体想要知道*的分区计划可能会对她们造成多少损害。帮助她们求出最坏情况,也就是更赛牛较多或是均势的区的最小可能的数量。
题目分析
看完题目,我们可以明显得到一个 O(NK) 的dp。
令 dp[i] 表示 1~i 中G牛较多或是均势的区的最小可能的数量。sum[i]表示 1~i 中H牛的数量减去G牛的数量。
显然 dp[i] = min( dp[i-j] + (sum[i]-sum[j])<=0?1:0 , dp[i] )。
考虑如何优化dp。
发现,dp[i]的值仅与 dp[i-k+1] ~ dp[i-1] 的值有关,所以我们会想到用单调队列优化dp。
单调队列(小根堆)中存两个值,分别为 位置x 与 dp[x] 的值。dp[x]为第一关键字,越小越靠前, sum[x]为第二关键字(存的时候为x,方便弹出)
于是dp方程可以改写为 dp[i] = sum[i] - sum[q.top().y] <=0 ? q.top().x+1 : q.top().x;
然后我们就可以在O(n ⋅ logk)的复杂度内完成dp了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=3e5+10; 4 5 int n,k; 6 int f[MAXN],sum[MAXN]; 7 char s[MAXN]; 8 9 struct Node{ 10 int x,y; 11 inline bool operator <(const Node &a)const{ 12 if(x==a.x) return sum[y]>sum[a.y]; 13 else return x>a.x; 14 } 15 }; 16 priority_queue<Node> q; 17 int main(){ 18 freopen("redistricting.in","r",stdin); 19 freopen("redistricting.out","w",stdout); 20 scanf("%d%d%s",&n,&k,s+1); 21 for(int i=1;i<=n;++i) 22 sum[i]=s[i]=='H'?sum[i-1]+1:sum[i-1]-1; 23 memset(f,0x3f,sizeof(f)); 24 f[0]=0; 25 q.push((Node){0,0}); 26 for(int i=1;i<=n;++i){ 27 while(q.top().y<i-k) q.pop(); 28 f[i]=sum[i]-sum[q.top().y]<=0?q.top().x+1:q.top().x; 29 q.push((Node){f[i],i}); 30 } 31 printf("%d\n",f[n]); 32 return 0; 33 }