题目:
分析:
因为原序列是一个环,所以要断环为链,将序列复制一份放在后面。
显然将R移动到一块的同时,B也会在一块,所以只需要求R移动到一起的贡献即可。
枚举一个分界点,让这个点左边所有的R都向左靠,右边所有的R都向右靠。这时候一定是满足题意的。
但会发现,同一个分界点,随着断环的位置改变,统计出来的答案也会改变,所以还要枚举一个断环点。
复杂度是n^2
考虑优化:
RRBBRBBBR 像这样一组数据,把将R向两边放看做是将B往中间靠,那么在第5个位置是最优的(左边移两格,右边不需要移)。
当断环的点向右移,lr不会增加,rr不会减少,相当于如果分界点向左移动,只会给右边带来更多的花费,也不会给左边减少花费(因为移动的是最左边的点)。
所以断环的点不会从原来的位置向左移动。
显然我们不需要将分界点从头枚举到尾,我们只需要找到一个最大的位置,当答案不再减小,就break掉(也就是说答案随分界点的变化是单调的)。
复杂度:接近O(n)
实现流程:
定义lr为左边R的个数,rr为右边R的个数,r为当前所需花费。枚举断环点,枚举分界点,统计答案。
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 1000005 #define ri register int char s[N<<1]; void work() { int n=strlen(s+1); for(ri i=1;i<=n;++i) s[i+n]=s[i]; //断点指断环的点 分界点指不移动的点 将所有的B往中间分界点靠 R向两边靠 int lr=0,rr=0; ll r=0,ans; for(ri i=1;i<=n;++i) if(s[i]=='R') r+=n-i-rr,rr++;//预处理断点在1的情况 //printf("%lld ",rr); ans=r; int now=1; for(ri pos=1;pos<=n;++pos){//移动断点 while(now<n+pos){//枚举分界点 跳指针移动 if(s[now]=='R'){//移动B不影响 移动R会对lr 与 rr的统计有影响 ll tmp=r; rr--;//移到了一个R 所以先右边的-- 减去它自己 便于下面统计 注意分界点这个点是包括在右区间里面的 tmp+=now-pos-lr-(pos+n-1-now-rr);//pos是它目前的起点 pos+n-1是目前的终点 //统计步数:这个R本来有移到右边的贡献 现在减去 加上移到左边的贡献 lr++;//左边的++ 便于上面的统计 if(tmp<=r) r=tmp;//如果不能更新 就还原 然后break else{ lr--; rr++; break; } } now++; } ans=min(ans,r); if(s[pos]=='R') lr--,rr++;//如果把R移过去了 右边的R++ 左边-- else r+=rr-lr;//如果移动的是B 就要加上所有右边的R移动到右边的贡献 减去左边的R移动到左边的贡献 } printf("%lld ",ans); } int main() { freopen("sushi.in","r",stdin); freopen("sushi.out","w",stdout); int T; scanf("%d",&T); while(T--){ scanf("%s",s+1); work(); } } /* 1 BBRBBRBBBRRR */