【vijos1900】 学姐吃寿司
https://vijos.org/p/1900 (题目链接)
题意
给出一个01环,问最少经过多少次相邻互换使得所有的0聚在一坨,所有的1聚在一坨。
Solution
源自:LCF Solution
一般环上的问题都要把序列复制一遍,然后转成了序列上的问题。这道也不例外。
经过观察后我们“显然”可以得到一个结论:对于最优解,定有断点使得所有的交换都不经过这个点。因为如果所有相邻的位置都交换了一次,相邻的位置都交换了一次那么这些操作并没有改变这个环的形态,也就是没有任何作用完全可以直接去掉。(然而LCF讲题翻车,这个证明显然是错误的,在不同的时间交换两个相邻的位置最终态并不会一样→_→。然而我也会证,只是感觉很正确)
于是,我们就可以枚举断点,然后就转变为了一个序列了。我们的目的就变成了使0全部靠边。然后可以预处理出每个0移动到左边界需要多少步,右边界需要多少步。发现对于一个序列,肯定是存在一条分界线使得左边一部分0往左靠,右边一部分0往右靠,当断点顺时针移动的时候,分界点显然不会逆时针移动。于是这玩意儿是有单调性的,弄一个单调指针扫一扫就可以了。
细节
数组开大两倍
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // vijos1900 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define inf 1e18 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=2000010; int a[maxn],sum[maxn]; char ch[maxn]; int main() { int T; scanf ( "%d" ,&T); for ( int cas=1;cas<=T;cas++) { scanf ( "%s" ,ch+1); int n= strlen (ch+1); for ( int i=1;i<=n;i++) { if (ch[i]== 'B' ) a[i]=0; else a[i]=1; } for ( int i=n+1;i<=n*2;i++) a[i]=a[i-n]; for ( int i=1;i<=n*2;i++) sum[i]=a[i]+sum[i-1]; LL p=1,ans=inf,L=0,R=0,lnum=0,rnum=0; for ( int i=1;i<=n;i++) if (a[i]==0) R+=sum[n]-sum[i-1],rnum++; for ( int s=1;s<=n;s++) { int t=s+n-1; if (s!=1) { if (a[s-1]==0) lnum--; if (a[s-1]==1) L-=lnum; if (a[t]==0) rnum++; if (a[t]==1) R+=rnum; } for (p;p<=t+1;p++) if (a[p]==0) { if (sum[p-1]-sum[s-1]>sum[t]-sum[p]) break ; L+=sum[p-1]-sum[s-1]; R-=sum[t]-sum[p]; lnum++;rnum--; } ans=min(ans,L+R); } printf ( "Case #%d: %lld\n" ,cas,ans); } return 0; } |
This passage is made by MashiroSky.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步