乱搞 寿司

来源:互联网 发布:20174g网络哪家网速快 编辑:程序博客网 时间:2024/04/26 17:13

题面去内网找。。
第一思路当然是找一个位置成为断点,让所有的移动都不经过它,让一部分点到这个断点的两侧。很明显是有符合方案的,并且在所有枚举中有一种是最优解。
于是我考试时打了O(N^2)的暴力。。。。枚举每一个点是要移动到左边还是右边。而实际上是可以找到一个边界之后用前缀和,找边界可以二分。O(N*logN),如果数据水可以卡过去。
但是,如果移动红点,枚举每一个蓝点作为断点,边界就是最中间一个蓝点的位置(在这里左右蓝点数一样,向左一个红点就一定向左移动比较优,向右同理)。而断点向右移动一个,边界也会移动一个蓝点,那么二分干嘛。。直接找就好了。
以蓝点记录区间里红点的个数,拉成链(可以考虑把最后一个蓝点搞成第0个蓝点),然后搞个前缀和。维护一个sum的值表示搞到这里的答案,找到边界后,因为是断点右移,那么边界以左的红点走的步数-1,以右的+1.因为有红点的前缀和,搞起来就很容易了。
还有一个坑,边界右移后会越过一些红点,导致它们的答案压根不会发生改变。但是如果蓝点是偶数个,位于两边界间的红点其实是在真正的边界上(最中间应该是两蓝点的中间部分,两蓝点都在边界两侧),这些红点对于边界在他左边一个和在他右边一个都是一样的。但是如果有奇数个蓝点就不太一样了,就要减去多出来的(也就是当他在边界以右,而实际上越到边界左侧的红点,他们对答案并没产生额外贡献)

#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>#include <cstring>#define ll long long#define N 1001005using namespace std;int t,n;char s[N];ll b,r,ans,now,a[N*2];int main(){    scanf("%d",&t);    while(t--)    {        scanf("%s",s+1);n=strlen(s+1);b=r=now=ans=0;        memset(a,0,sizeof(a));        for(int i=1;i<=n;i++)            if(s[i]=='R')r++,a[b]++;            else b++;        for(ll i=1;i<b;i++)now+=min(i,b-i)*a[i];        ans=now;a[0]+=a[b];        for(int i=b;i<b*2;i++)a[i]=a[i-b];        for(int i=1;i<b*2;i++)a[i]+=a[i-1];        for(int i=1;i<b;i++)        {            ll l=a[i+(b>>1)-1]-a[i-1];now-=l;now+=r-l;            if(b&1)now-=a[i+(b>>1)]-a[i+(b>>1)-1];            ans=min(ans,now);        }        printf("%lld\n",ans);    }}
原创粉丝点击