• Educational Codeforces Round 81


    A

    题意

    用电子屏显示数字,0-9一个数字需要若干段,现在总共可以显示n段,求最大可以显示的数字

    思路

    显然位数的贡献必定优于一位的数字大小,所以贪心用需要段数最小的数字(1)扩展位数,最后如果剩一段就把第一位变为7

    代码

    #include<bits/stdc++.h>
    using namespace std;
     
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            int n,len;
            cin>>n;
            len=n/2;
            if(n&1)
                cout<<'7';
            else
                cout<<'1';
            for(int i=1;i<len;i++)
                cout<<'1';
            cout<<endl;
        }
    }
    

    B

    题意

    给一个由0,1组成的串s,把s重复无数次得到t,求t有多少前缀使得该前缀的balance值为x,一个字符串的balance值即该串01数量差。若有无穷多则输出-1

    思路

    t的前缀必定是ks串加一个s的前缀组成,所以t前缀的balance值也由ksbalance值(记为sbalance)与s某一前缀的balance值确定。
    所以处理一下s前缀的balance值,然后看有多少个前缀的balance满足:存在一个非负整数k使得 k sbalance +当前前缀balance == x
    然后考虑无限的情况,如果sbalance非零,则t的前缀一定是随长度单调的,所以无限就转化为了 sbalance为零,并且存在一个前缀的balance == x

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAX=1e5+5;
     
    char s[MAX];
    int b[MAX];
     
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,x,sum=0,res=0;
            scanf("%d%d%s",&n,&x,s+1);
            if(x==0)res++;
            for(int i=1; i<=n; i++)
            {
                sum+=s[i]-'0';
                b[i]=i-2*sum;
            }
            if(b[n]!=0)
                for(int i=1; i<=n; i++)
                {
                    int cur=x-b[i];
                    if(max(cur,b[n])>0&&min(cur,b[n])<0)continue;
                    if(cur%b[n]==0)res++;
                }
            else
                for(int i=1; i<=n; i++)if(x==b[i])res=-1;
            printf("%d
    ",res);
        }
    }
    

    C

    题意

    给两个字符串s,t,要求构造一个z == t,每次操作可以从s中选一个子序列放到z末尾,求最少多少次操作使得z == t

    思路

    可以构造一个nxt数组,代表s的子序列,然后贪心的匹配尽可能多的t中的字符,每次匹配到末尾操作数++。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAX=1e5+5;
    
    char s[MAX],t[MAX];
    int nxt[MAX][26];
     
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s%s",s+1,t+1);
            s[0]=t[0]='!';
            int len=strlen(s);
            for(int i=0;i<26;i++)
                nxt[len-1][i]=-1;
            for(int i=len-2;i>=0;i--)
            {
                for(int j=0;j<26;j++)
                    nxt[i][j]=nxt[i+1][j];
                nxt[i][s[i+1]-'a']=i+1;
            }
            int p=0,res=0;
            len=strlen(t);
            for(int i=1;i<len;i++)
            {
                int cur=t[i]-'a';
                p=nxt[p][cur];
                if(p==-1)res++,p=0;
                else continue;
                p=nxt[p][cur];
                if(p==-1){res=-2;break;}
            }
            printf("%d
    ",res+1);
        }
    }
    

    D

    题意

    给两个整数a,m,求有多少x ∈[0,m) 使得gcd(a,m) == gcd(a+x,m)

    思路

    gcd(a,m)d,则有如下结论:

    • dx因数
    • (a+x)/dm/d互质

    再设(a+x)/dt,有推论如下:

    [xin[d,m)\ t in[frac ad+1,frac{a+m}d)\ 即\ t in [1,frac md) ]

    则现在问题转化为求有多少t ∈[1,m/d),满足tm/d互质,即就是欧拉函数。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
    ll euler_phi(ll x)
    {
        ll res=x;
        for(ll i=2;i*i<=x;i++)
            if(x%i==0)
            {
                res=res/i*(i-1);
                while(x%i==0)x/=i;
            }
        if(x>1)res=res/x*(x-1);
        return res;
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            ll a,m;
            scanf("%I64d%I64d",&a,&m);
            printf("%I64d
    ",euler_phi(m/gcd(a,m)));
        }
    }
    

    E

    题意

    给一个1-n的排列,每个数有a[i]的代价值,现在可以把该排列任意划分为左右两个非空集合,然后可以将数字转移到另一个集合里,转移第i个(不是数字i)数字需要的代价为a[i],最后要求使得左边集合所有数小于右边集合,或者一集合为空,求如何转移代价最小。

    思路

    由于给出的是一个排列,所以可以想到,假设左边集合长度为k (k>0),则最后左边集合结果一定为1-k。所以可以考虑枚举最后左边的结果是怎么样的,再枚举从哪里分开后转移得到这个结果。但是这样是n^2,肯定不行。所以考虑把一个n的枚举优化成log(n)的。

    搞一个pre数组为a数组的前缀,pre[i]代表分界点在i(左包括),并且将1-i的数全部移到右集合的代价。对pre建立一棵线段树。

    然后递推最后的结果。对于一个数x,设x位置为p[x],则更新1 -- p[x]-1加上a[p[x]],代表把左边的这些数转移到右边的同时需要把x转移到左边,然后更新p[x] -- n-1减去a[p[x]],代表分界点在这里的时候x不需要往右边移动。然后线段树求全局最小值,就达到了枚举分界点取最小值的目的。然后整个可以递推,从1开始枚举,每次处理一个x,总时间复杂度O(nlogn)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAX=2e5+5;
    typedef long long ll;
     
    int p[MAX],a[MAX];
    ll add[MAX<<2],pre[MAX];
    struct data
    {
        ll minn;
    } tree[MAX<<2];
    
    void pushup(int rt)
    {
        int ls=rt<<1,rs=rt<<1|1;
        tree[rt].minn=min(tree[ls].minn,tree[rs].minn);
    }
    void pushdown(int rt)
    {
        if(add[rt]==0)return ;
        int ls=rt<<1,rs=rt<<1|1;
        tree[ls].minn+=add[rt];
        tree[rs].minn+=add[rt];
        add[ls]+=add[rt];
        add[rs]+=add[rt];
        add[rt]=0;
    }
    void build(int l,int r,int rt)
    {
        if(l==r)
        {
            tree[rt].minn=pre[l];
            return;
        }
        int m=(l+r)>>1;
        build(l,m,rt<<1);
        build(m+1,r,rt<<1|1);
        pushup(rt);
    }
    void update_segment(int L,int R,int C,int l,int r,int rt)
    {
        if(L<=l&&r<=R)
        {
            tree[rt].minn+=C;
            add[rt]+=C;
            return;
        }
        pushdown(rt);
        int m=(l+r)>>1;
        if(L<=m)
            update_segment(L,R,C,l,m,rt<<1);
        if(R>m)
            update_segment(L,R,C,m+1,r,rt<<1|1);
        pushup(rt);
    }
     
    int main()
    {
        int n,x;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&x);
            p[x]=i;
        }
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            pre[i]=pre[i-1]+a[i];
        }
    	pre[n]=a[n];
        build(1,n,1);
        ll res=a[1];
        for(int i=1; i<n; i++)
        {
            if(p[i]>1)
                update_segment(1,p[i]-1,a[p[i]],1,n,1);
            if(p[i]<n)
                update_segment(p[i],n-1,-a[p[i]],1,n,1);
            res=min(res,tree[1].minn);
        }
        printf("%I64d
    ",res);
    }
    
  • 相关阅读:
    解决Original error: Could not proxy command to remote server. Original error: Error: socket hang up
    python各进制转换
    爬楼梯问题,yield学习总结
    微信开放平台API开发资料
    数据切分——Atlas读写分离Mysql集群的搭建
    svn SSL 错误:Key usage violation in certificate has been detected
    如何将一个空间里的内容全部复制到另一个空间,文件名不变
    SVN客户端安装 Linux
    Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
    搭建SVN服务器
  • 原文地址:https://www.cnblogs.com/cryingrain/p/12657275.html
Copyright © 2020-2023  润新知