• NOIP模拟测试8「寿司」


    考试时打的类似$n^2$暴力,然后炸了只有10分

    后来验证我的算法伪了。

    题解


    显然你有一种解法,假设你要在一个B点断开将R分别移向最左 最右,这样只用分别计算B点右面蓝色数量左面蓝色数量就得到了一个ans

    这个题有一个很不显然的结论,假设你要将R移向两边时,序列唯一确定时,设pos=(蓝色数量+1)/2,在pos点将R移向左面,右面花费最小(单调性)

    因为这个序列是循环的所以我们只要枚举B点断开的位置就可以$n^2$求出最小的ans值

    $n^2$显然过不了1000000

    现在我们思考$n^2$问题在哪,首先你每次重新计算一次ans额外花费了时间,然后每次都枚举断点又花费了时间

    但实际上我们每次循环到下一个这个序列实际上变化很少,只是前面那个字母删去,后面再加一个字母

    实际对ans改变也很少

    类似于莫队,对于ans+ - 我们可以得到另一个ans

    那么我们可以计算出他的改变

    首先我们可以得出每次循环到下一B,假设我们目前断点不变,那么所有左边R对ans造成贡献都会减1,所有右边R对ans造成贡献都会加1

    然后我们思考下一个断点,假设当前断点显然就是下一个B

    然后我们断点移动过程中我们发现有一些R从右面移动到了左面,那么他要移动贡献也从右面blue数量改成了右面贡献,减去左面贡献加上左面的贡献即可

    代码


    #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
    ",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
    ",rr[j],rr[n+1]);
                    }
                    break;
                }
                else if(c[i]=='R')ans+=lb[i];
            }
    //        printf("ans=%lld
    ",ans);
            zo=ans;
            ll head=1,tail=n;
            while(head<=n){
                if(c[head]=='B'){    
                    ans-=lr[p]-lr[head-1];//如果当前为B将B向后移动那么左边所有R代价-1
                    ans+=rr[p]-rr[tail+1];//如果当前为B将B向后移动那么右边所有R代价+1
                    while(c[++p]!='B'){//当前指针应当指向下一个B
                        ans+=lb[p]-lb[head];//如果R由左变为了右,那么代价从左边变成右边
                        ans-=rb[p]-rb[tail+2];
                    }
                }
                head++,tail++;
                zo=min(zo,ans);
            }
            cout<<zo<<endl;
        }
    }
    View Code
    我已没有下降的余地
  • 相关阅读:
    POJ 2260
    安防监控 —— 数据上行刷新与命令下发过程
    安防监控 —— 主框架搭建
    安防监控 —— 软硬件环境分析与通信协议制定
    安防监控项目 —— 需求分析
    安防监控项目 —— 环境搭建
    Linux下I2C总线驱动框架
    IIC总线硬件工作原理(待完善)
    linux驱动面试(转)
    驱动开发 —— 输入子系统(工作逻辑分析)
  • 原文地址:https://www.cnblogs.com/znsbc-13/p/11247211.html
Copyright © 2020-2023  润新知