zoukankan      html  css  js  c++  java
  • 寿司

    大体意思是让字符串的两种字符串分开。首先,可想到处理环的问题,转换成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)查询都挺神的。

  • 相关阅读:
    类和结构体的区别
    List中的select和where 方法
    js报错,Uncaught SyntaxError: Unexpected token }
    去除list集合中重复项的几种方法
    new
    Dynamic
    ie8下缓存
    datagrid中动态url
    从后台序列化后的类的字符串
    抽象工厂
  • 原文地址:https://www.cnblogs.com/2018hzoicyf/p/11248012.html
Copyright © 2011-2022 走看看