大体意思是让字符串的两种字符串分开。首先,可想到处理环的问题,转换成2*n-1的序列问题。
手玩的几个性质:
1.最优的方案一定只有不同颜色的寿司交换
2.一个寿司的移动步数可以O(1)求
3.一定存在一个断点使得所有蓝色的移向两侧(假设动蓝色的)(和全都移向断点一样)
4.只考虑蓝色的和只考虑红色的效果一样
40%
我们为了确保所有的方案都能统计到,先枚举断点,再枚举这种情况下的左端点。(起初,并没有理解为什么要枚举左端点,其实,是因为断点是移项中不会经过的点,但所谓的环断成链以后,不能确保最优的聚合的中间点是谁)。O(n^2)
100%
(learn from xm大神)
设左端点为断点,中间点为zh
![](/qrcode.jpg)
能发现40%算法的问题在于要枚举的情况有点多,考虑一种性质:一个B移动的最优时,一定只和R交换,所以他的移动步数为左/右取min。再往深的一层说,一个固定的序列,你会发现随着下标递增,B左边的R数在增多,右边的R数在减少。(显然有单调性)B肯定往R少的一侧去,那么当枚举到一个l时,zh就可以二分查找了。
emmm。。。然而还不够,在这种优秀的O(n*logn)下还不能O(1)查询一种情况的贡献。
此时,前缀和思想可以实现(不是普通的前缀和)
设qsm[i]为前缀R的个数,qqsm[i]为将1~i的B点全部移到最左边的代价。
那么将zh到l的点移到左边的代价为
qqsm[zh]-qqsm[l-1]-qsm[l-1]*(zh-l+1-qsm[zh]+qsm[l-1])
解释一下:简单的qqsm[zh]-qqsm[l-1]并不对,为什么?考虑此时只是将l~zh的B点移动到了最左边,而不能保证,移动到以l为界限的左边,此时l~zh中每一个B会多走1~l-1中R的个数步,也要减去。
最后:
#include<cstdio> #include<cstring> #include<iostream> #include<vector> #define MAXN 2000010 #define ll long long #define INF 0x7ffffffffff using namespace std; ll T,len; char rn[MAXN]; ll ans; ll cnt; double xian; ll qsm[MAXN],hsm[MAXN]; ll qqsm[MAXN],hhsm[MAXN]; inline ll minn(ll a,ll b){ return a<b?a:b; } ll erfen(ll l,ll r,ll bi){ if(l==r)return l; ll mid=l+r>>1; if(qsm[mid]-qsm[bi-1]>xian) return erfen(l,mid,bi); else if(qsm[mid]-qsm[bi-1]<xian) return erfen(mid+1,r,bi); else return mid; } ll cal(ll l,ll zh,ll r){ ll res=0; res+=qqsm[zh]-qqsm[l-1]-qsm[l-1]*(zh-l+1-qsm[zh]+qsm[l-1]); res+=hhsm[zh+1]-hhsm[r+1]-hsm[r+1]*(r-zh-hsm[zh+1]+hsm[r+1]); return res; } int main(){ // freopen("da.in","r",stdin); scanf("%lld",&T); while(T){ --T; ans=INF; scanf("%s",rn+1); len=strlen(rn+1); cnt=0; qqsm[0]=qsm[0]=0; hhsm[2*len]=hsm[2*len]=0; ll lim=2*len-1; for(int i=1;i<len;++i) rn[i+len]=rn[i]; for(ll i=1;i<=lim;++i){ qsm[i]=qsm[i-1]+(rn[i]=='B'); if(i<=len)cnt+=(rn[i]=='B'); if(rn[i]=='R') qqsm[i]=qqsm[i-1]+qsm[i-1]; else qqsm[i]=qqsm[i-1]; } xian=cnt*0.5; for(ll i=lim;i>=1;--i){ hsm[i]=qsm[lim]-qsm[i-1]; if(rn[i]=='R') hhsm[i]=hhsm[i+1]+hsm[i+1]; else hhsm[i]=hhsm[i+1]; } /*for(int i=1;i<=lim;++i){ printf("i=%d %lld %lld %lld %lld\n",i,qsm[i],qqsm[i],hsm[i],hhsm[i]); }*/ ll zh; for(ll l=1;l<=len;++l){ zh=erfen(l,l+len-1,l); //cout<<l<<" "<<zh<<" "<<l+len-1<<endl; ans=minn(ans,cal(l,zh,l+len-1)); //cout<<cal(l,zh,l+len-1)<<endl; } printf("%lld\n",ans); } } /* R到左边的步数为左边B的个数 */
感觉决策单调性和O(1)查询都挺神的。