寿司

大体意思是让字符串的两种字符串分开。首先,可想到处理环的问题,转换成2*n-1的序列问题。

手玩的几个性质:

  1.最优的方案一定只有不同颜色的寿司交换

  2.一个寿司的移动步数可以O(1)求

  3.一定存在一个断点使得所有蓝色的移向两侧(假设动蓝色的)(和全都移向断点一样)

  4.只考虑蓝色的和只考虑红色的效果一样

40%

  我们为了确保所有的方案都能统计到,先枚举断点,再枚举这种情况下的左端点。(起初,并没有理解为什么要枚举左端点,其实,是因为断点是移项中不会经过的点,但所谓的环断成链以后,不能确保最优的聚合的中间点是谁)。O(n^2)

100%

  (learn from xm大神

  设左端点为断点,中间点为zh

  能发现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
",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
",ans);
	}
}
/*
	R到左边的步数为左边B的个数
*/

感觉决策单调性和O(1)查询都挺神的。

原文地址:https://www.cnblogs.com/2018hzoicyf/p/11248012.html