NOIP模拟测试8「寿司」
考試時打的類似$n^2$暴力,然后炸了只有10分
后來驗證我的算法偽了。
題解
?
顯然你有一種解法,假設(shè)你要在一個B點斷開將R分別移向最左 最右,這樣只用分別計算B點右面藍色數(shù)量左面藍色數(shù)量就得到了一個ans
這個題有一個很不顯然的結(jié)論,假設(shè)你要將R移向兩邊時,序列唯一確定時,設(shè)pos=(藍色數(shù)量+1)/2,在pos點將R移向左面,右面花費最小(單調(diào)性)
因為這個序列是循環(huán)的所以我們只要枚舉B點斷開的位置就可以$n^2$求出最小的ans值
$n^2$顯然過不了1000000
現(xiàn)在我們思考$n^2$問題在哪,首先你每次重新計算一次ans額外花費了時間,然后每次都枚舉斷點又花費了時間
但實際上我們每次循環(huán)到下一個這個序列實際上變化很少,只是前面那個字母刪去,后面再加一個字母
實際對ans改變也很少
類似于莫隊,對于ans+ - 我們可以得到另一個ans
那么我們可以計算出他的改變
首先我們可以得出每次循環(huán)到下一B,假設(shè)我們目前斷點不變,那么所有左邊R對ans造成貢獻都會減1,所有右邊R對ans造成貢獻都會加1
然后我們思考下一個斷點,假設(shè)當(dāng)前斷點顯然就是下一個B
然后我們斷點移動過程中我們發(fā)現(xiàn)有一些R從右面移動到了左面,那么他要移動貢獻也從右面blue數(shù)量改成了右面貢獻,減去左面貢獻加上左面的貢獻即可
代碼
?
#include<bits/stdc++.h> #define ll long long #define A 2100000 using namespace std; ll t,len,n,ans,pos,zo,p; ll lb[A],rb[A],lr[A],rr[A]; char c[A]; int main() { // freopen("mkd.txt","r",stdin); // freopen("wa.txt","w",stdout);scanf("%lld",&t);while(t--){ans=0;zo=ans;lb[0]=rb[0]=lr[0]=rr[0]=0;scanf("%s",c+1);len=strlen(c+1);n=len;pos=-1;for(ll i=1;i<=len;i++){c[n+i]=c[i];}len*=2;rr[len+1]=rb[len+1]=0;for(ll i=1;i<=len;i++){lb[i]=lb[i-1],lr[i]=lr[i-1];if(c[i]=='B')lb[i]++;else lr[i]++;}for(ll i=len;i>=1;i--){rr[i]=rr[i+1],rb[i]=rb[i+1];if(c[i]=='B') rb[i]++;else rr[i]++;}pos=(lb[n]+1)/2; // printf("pos=%lld\n",pos);for(ll i=1;i<=n;i++){if(lb[i]==pos){p=i;for(ll j=n;j>i;j--){if(c[j]=='R')ans+=rb[j]-rb[n+1]; // printf("rr=%lld rr[n]=%lld\n",rr[j],rr[n+1]); }break;}else if(c[i]=='R')ans+=lb[i];} // printf("ans=%lld\n",ans);zo=ans;ll head=1,tail=n;while(head<=n){if(c[head]=='B'){ ans-=lr[p]-lr[head-1];//如果當(dāng)前為B將B向后移動那么左邊所有R代價-1ans+=rr[p]-rr[tail+1];//如果當(dāng)前為B將B向后移動那么右邊所有R代價+1while(c[++p]!='B'){//當(dāng)前指針應(yīng)當(dāng)指向下一個Bans+=lb[p]-lb[head];//如果R由左變?yōu)榱擞?#xff0c;那么代價從左邊變成右邊ans-=rb[p]-rb[tail+2];}}head++,tail++;zo=min(zo,ans);}cout<<zo<<endl;} } View Code?
轉(zhuǎn)載于:https://www.cnblogs.com/znsbc-13/p/11247211.html
總結(jié)
以上是生活随笔為你收集整理的NOIP模拟测试8「寿司」的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 房屋的买卖合同备案登记表一般什么时候可以
- 下一篇: 房屋备案表黄色代表什么?