• BCPC2021预赛


    补题链接
    我无颜……

    [F]

    分析:

    关于“排名”没转化过来。
    实际上排名 = 小于等于自己的数的个数。那么可以针对每一个比自己小的数计算,考虑它们各自出现在多少个区间中,位置在自己左边和右边的分开计算。再加上自己的答案。
    于是用树状数组就能做了。时间复杂度 $ O(nlogn) $ 。

    代码如下
    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    int const N=5e5+5;
    int n,a[N];
    ll ans[N],tl[N<<1],tr[N<<1];
    ll ql(int x){ll ret=0; while(x){ret+=tl[x]; x-=(x&(-x));} return ret;}
    void addl(int x,int v){while(x<=n){tl[x]+=v; x+=(x&-x);}}
    ll qr(int x){ll ret=0; while(x){ret+=tr[x]; x-=(x&(-x));} return ret;}
    void addr(int x,int v){while(x<=n){tr[x]+=v; x+=(x&-x);}}
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            ans[i]=(ll)i*(n-i+1);
            ans[i]+=ql(a[i])*(n-i+1);
            addl(a[i],i);
        }
        for(int i=n;i;i--)
        {
            ans[i]+=qr(a[i])*i;
            addr(a[i],n-i+1);
        }
        for(int i=1;i<=n;i++)
            printf("%lld\n",ans[i]);
        return 0;
    }
    

    [G]

    分析:

    $ 2*10^5 $ 是可以考虑 $ O( n \sqrt{n} ) $ 的做法的。此题就可以按 $ |T_i| $ 是否大于 $ \sqrt{ |S| } $ 分开做。
    $ |T_i| \leq \sqrt{ |S| } $ 的,可以预处理出 $ f[len][i][c] $ 表示 $ mod(len) $ 下 $ i $ 位置上字符 $ c $ 在 $ S $ 中出现的次数。预处理的复杂度是 $ O( n \sqrt{n} ) $ ,每个这样的 $ T_i $ 计算答案的复杂度是 $ O( |T_i| ) $ ,即 $ O( \sqrt{n} ) $ 。
    $ |T_i| \geq \sqrt{ |S| } $ 的,直接做即可。每个这样的 $ T_i $ 计算答案的复杂度是 $ O(n) $ ,但是这样的串不超过 $ \sqrt{n} $ 个。
    然后总的复杂度是 $ O( n \sqrt{n} ) $ 。这种套路掌握得还是不熟……

    代码如下
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int const N=2e5+5,M=505;
    int n,m,ans[N],f[M][M][30];
    char s[N],t[N];
    void Init()
    {
        m=500;
        for(int i=1;i<=n;i++)
        {
            int c=s[i]-'a';
            for(int j=1;j<=m;j++) f[j][(i-1)%j][c]++;
        }
    }
    int main()
    {
        scanf("%s",s+1); n=strlen(s+1);
        Init();
        int q; scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {   
            scanf("%s",t+1); int len=strlen(t+1);
            int ans=0;
            if(len<=m)
            {
                for(int j=1;j<=len;j++)
                    ans+=f[len][j-1][t[j]-'a'];
            }
            else
            {
                int k=1;
                for(int i=1;i<=n;i++)
                {
                    ans+=(s[i]==t[k]); k++;
                    if(k>len)k=1;
                }
            }
            printf("%d\n",n-ans);
        }
        return 0;
    }
    

    [H]

    分析:

    把能否杀死对方转化成两方各自参数(攻击*血量)的比较,就可以确定要选定的随从是哪个(攻击*血量最大或次大的才有能力杀死其他所有人);
    选定的随从杀死其他所有人的过程中受到的伤害是固定的,其中还会有个最大的伤害。根据这个就能判断能否达成题目要求了。
    注意判断攻击等于0的情况。
    然而一直WA在第45个点,不知为啥。先放在这吧……

    代码如下
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    int const N=1e5+5;
    struct Nd{
        ll a,h;
    }a[N];
    int n;
    bool cmp(Nd x,Nd y){return (x.a*x.h==y.a*y.h) ? x.a>y.a : x.a*x.h>y.a*y.h;}
    ll cal(ll h,ll a)
    {
        if(a==0)return -1;
        return (h/a)+(h%a!=0);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld%lld",&a[i].a,&a[i].h);
        sort(a+1,a+n+1,cmp);
        if(n==1){puts("NO"); return 0;}
        // if(n>=3 && (a[1].a*a[1].h==a[2].a*a[2].h) && (a[2].a*a[2].h==a[3].a*a[3].h)){puts("NO"); return 0;}
    
        if(a[1].a==0 || a[2].a==0){puts("NO"); return 0;}
    
        ll mx=0,all=0; int p; //最大伤害,总伤害,最大伤害者
    
        for(int i=2;i<=n;i++)
        {
            ll t=cal(a[i].h,a[1].a);
            all += t*a[i].a;
            if(t*a[i].a>=mx)mx=t*a[i].a,p=i;
        }
        // if(a[1].h-all<=0 && a[1].h-all+mx>0 && cal(a[1].h-all+mx,a[p].a)==cal(a[p].h,a[1].a))
        //     {puts("YES"); return 0;}
        if(a[1].h-all<=0 && a[1].h-all+mx>0)
        {
            for(int i=2;i<=n;i++)
            {
                ll t=cal(a[i].h,a[1].a);
                if(t*a[i].a==mx && cal(a[1].h-all+mx,a[i].a)==t)
                    {puts("YES"); return 0;}
            }
        }
    
        mx=0; all=0; //最大伤害,总伤害
        for(int i=1;i<=n;i++)
        {
            if(i==2)continue;
            ll t=cal(a[i].h,a[2].a);
            all += t*a[i].a;
            if(t*a[i].a>=mx)mx=t*a[i].a,p=i;
        }
        // if(a[2].h-all<=0 && a[2].h-all+mx>0 && cal(a[2].h-all+mx,a[p].a)==cal(a[p].h,a[2].a))
        //     {puts("YES"); return 0;}
        if(a[2].h-all<=0 && a[2].h-all+mx>0)
        {
            for(int i=1;i<=n;i++)
            {
                if(i==2)continue;
                ll t=cal(a[i].h,a[2].a);
                if(t*a[i].a==mx && cal(a[2].h-all+mx,a[i].a)==t)
                    {puts("YES"); return 0;}
            }
        }
        puts("NO");
        return 0;
    }
    

    [F]

    分析:

    终态就是左右横跳。想想可以发现只要纵坐标能相差2,左右横跳就没问题。
    一开始向上跳可以确定对方在自己上面还是下面;然后借助初始距离大于4,再上跳一次或者下跳两次就能保证纵坐标相差2。

    代码如下
    #include<iostream>
    #include<cstdio>
    using namespace std;
    int main()
    {
        int d,d2;
        scanf("%d",&d);
        printf("U\n"); fflush(stdout);
        scanf("%d",&d2);
        if(d2==d){printf("U\n"); fflush(stdout);}
        else
        {
            printf("D\n"); fflush(stdout);
            scanf("%d",&d);
            printf("D\n"); fflush(stdout);
        }
        int f=0;
        while(1)
        {
            scanf("%d",&d);
            if(d==0||d==-1)break;
            printf("%c\n",(f)?'L':'R');
            fflush(stdout);
            f^=1;
        }
        return 0;
    }
    

    [J]

    分析:

    第一步分析答案是怎样组成的。对于移动第 $ i $ 个文件而言,对答案的贡献是:( 之前移动的文件中 $ q_j > q_i $ 的个数 $ ) $ + $ ( $ 之前没动(之后移动)的文件中 $ p_j < p_i $ 的个数 ) $ + $ ( $ 1 $ (从移动后文件跨越到未移动文件) )。这点我也是容易想到的。
    但是,最后一次不需要移动回未移动文件区域;然后我的思路就乱了。实际上这里可以先给问题加个限制条件,要求最后一次也要移动回未移动文件区域,得到做法后再考虑去掉条件。
    加上限制条件后,可以把答案写出式子来了;然后经过一些化简,可以发现按二位偏序的拓扑序移动是最优的。这样去掉限制条件也变得容易了,此题就做完了。题解讲得很清楚,赞。
    心得就是:在基础思路上,我还需要:加限制条件,写式子化简。

  • 相关阅读:
    PYTOHN1.day14
    PYTHON1.day13
    PYTHON1.day12
    PYTHON1.day11(n)
    PYTHON1.day10
    PYTHON1.day09
    PYTHON1.day08
    同步代码块 synchronized
    守护线程
    休眠线程
  • 原文地址:https://www.cnblogs.com/Zinn/p/15647244.html
Copyright © 2020-2023  润新知