• Nowcoder 牛客练习赛23


    Preface

    终于知道YKH他们为什么那么喜欢打牛客网了原来可以抽衣服

    那天晚上有空就也去玩了下,刷了一波水TM的YKH就抽到了,我当然是没有

    题目偏水,好像都是1A的。才打了一个半小时,回家就直接睡觉了


    A 托米的赌球

    送分题,考虑贪心的思想,由于要总数量最小,因此面额大的应该能选就选

    所以一路贪心下来即可。 CODE

    #include<cstdio>
    #include<cctype>
    using namespace std;
    const int A[7]={100,50,20,10,5,2,1},B[6]={50,20,10,5,2,1};
    int n,a,b,p;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
        if (x>9) write(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n);
        while (n--)
        {
            read(a); read(b); p=0;
            for (i=0;i<7;++i) write(a/A[i]),putchar(' '),a%=A[i];
            for (i=0;i<5;++i) write(b/B[i]),putchar(' '),b%=B[i]; write(b/B[5]); putchar('
    ');
        }
        return 0;
    }
    

    B 托米的划分

    首先我们发现一个十分显然的性质,每次划分时的两个数应当尽可能接近,这样乘积才会最小。

    可以结合长方形周长确定,两边越接近面积越小来理解当然用二次函数证明之也可

    然后考虑直接递归计算这个过程,这样貌似加上记忆化都是(O(n))的。

    随即我们发现这个可以打表,但是同样(O(n))的时空复杂度难以接受。

    然后我们就要发挥一下乱搞的技巧了,我们把(10^6)以内的数打表出来,然后剩下的直接递归处理即可。

    这样递归层数很小足以通过。 CODE

    #include<cstdio>
    #include<cctype>
    using namespace std;
    const int N=1e6;
    int t,n; long long d[N+5];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(long long x)
    {
        if (x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline void init(void)
    {
        for (register int i=1;i<=N;++i)
        d[i]=d[i+1>>1]+d[i>>1]+1LL*(i+1>>1)*(i>>1);
    }
    inline long long solve(int x)
    {
        if (x<=N) return d[x];
        return solve(x+1>>1)+solve(x>>1)+1LL*(x+1>>1)*(x>>1);
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(t); init();
        while (t--)
        {
            read(n); write(solve(n)); putchar('
    ');
        }
        return 0;
    }
    

    C 托米的位运算

    考虑转化问题,当给价最大化是,就是当它们的共同二进制位最大时就是最大给价。

    所以贪心地从大到小枚举二进制位,满足以下条件即可:

    • (a_i)最大二进制位为(1)
    • 满足上一条的(a_i)(operatorname{and})所得剩下的公有二进制位不小于目前的最高位

    然后就轻松水过了。 CODE

    #include<cstdio>
    #include<cctype>
    using namespace std;
    typedef long long LL;
    const LL N=1e5+5;
    LL t,n,a[N],b[N],cnt,tot;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(LL &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(LL x)
    {
        if (x>9) write(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register LL i,j; read(n);
        for (i=1;i<=n;++i) read(a[i]);
        for (cnt=0,tot=(1LL<<31)-1,j=30;j>=0;--j)
        {
            for (cnt=0,i=1;i<=n;++i)
            if (a[i]&(1LL<<j)) b[++cnt]=a[i],tot&=a[i];
            if (tot%(1LL<<j)==0)
            {
                for (printf("%lld
    ",cnt),i=1;i<cnt;++i)
                printf("%lld ",b[i]); printf("%lld",b[cnt]);
                break;
            }
        }
        return 0;
    }
    

    D 托米的咒语

    本来可能还想骗我们写写DFS增加下码量的,然后我用next_premutation水过去了

    数据范围这么小,因此全排列来一发,考虑怎么判断

    我们对于原来的字符串处理两个数组。(f_{i,j})表示第(i)之后(不包括(i)最近的一个(j)字符的位置,(fir_i)表示字符(i)在串中最早出现的位置。

    判断时直接(O(9))向后跳即可,CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=3005,P=15;
    char s[N]; bool flag;
    int f[N][P],num[P],fir[P],ans,n,now;
    int main()
    {
        register int i; scanf("%s",s+1); n=strlen(s+1);
        memset(f,63,sizeof(f));
        for (i=n-1;i>=1;--i)
        {
            memcpy(f[i],f[i+1],sizeof(f[i])); f[i][s[i+1]-'a'+1]=i+1;
            fir[s[i]-'a'+1]=i;
        }
        for (i=1;i<=9;++i) num[i]=i;
        do
        {
            for (now=fir[num[1]],flag=1,i=2;i<=9;++i)
            if (f[now][num[i]]<=n) now=f[now][num[i]]; else { flag=0; break; }
            ans+=flag;
        }while (next_permutation(num+1,num+10));
        return printf("%d",ans),0;
    }
    

    E 托米的数学

    貌似是不可食用的数学题不会留着以后填坑


    F托米的游戏

    这题如果你光用脑子想可能会感到毫无头绪,但是如果拿起笔写一下式子就会发现这是个SB题。

    考虑利用期望的线性性质来分析,总的期望轮数(E)等于各点的期望轮数(e_i)之和。

    我们先DFS求出每个点的深度(dep_i(dep_{rt}=1)),那么对于可能的情况只有两种:

    1. 选中该点导致该点被砍去,那么此时的概率(frac{1}{dep_x})(因为删去点的过程只与这个点的祖先个数(即深度)有关),期望贡献(1),则期望为(frac{1}{dep_x})
    2. 选到该点的祖先导致该点被砍去,此时概率都不用算,因为期望贡献(0),乘一下就是(0)

    因此答案就是(sum_{i=1}^n frac{1}{dep_i})

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    using namespace std;
    const int N=100005,mod=998244353;
    struct edge
    {
        int to,next;
    }e[N<<1];
    int n,dep[N]={0,1},head[N],cnt,x,y,ans;
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch; while (!isdigit(ch=tc()));
        while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void double_add(int x,int y)
    {
        e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
        e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt;
    }
    inline void inc(int &x,int y)
    {
        if ((x+=y)>=mod) x-=mod;
    }
    inline int quick_pow(int x,int p)
    {
        int tot=1;
        for (;p;x=1LL*x*x%mod,p>>=1) if (p&1) tot=1LL*tot*x%mod;
        return tot;
    }
    inline void DFS(int now,int fa)
    {
        inc(ans,quick_pow(dep[now],mod-2));
        for (register int i=head[now];~i;i=e[i].next)
        if (e[i].to!=fa) dep[e[i].to]=dep[now]+1,DFS(e[i].to,now);
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n); memset(head,-1,sizeof(head));
        for (i=1;i<n;++i) read(x),read(y),double_add(x,y);
        return DFS(1,-1),printf("%d",ans),0;
    }
    

    Postscript

    好像Rank19吧比赛的时候后两题没写。反正没抽到衣服就是不高兴。

  • 相关阅读:
    2020.4.26 resources
    Visual Studio M_PI定义
    12.3 ROS Costmap2D代价地图源码解读_1
    Delphi GDI对象之剪切区域
    用GDI+DrawImage画上去的图片会变大
    简单的GDI+双缓冲的分析与实现
    双缓冲绘图
    C++中的成员对象
    鼠标在某个控件上按下,然后离开后弹起,如何捕获这个鼠标弹起事件
    CStatic的透明背景方法
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9832588.html
Copyright © 2020-2023  润新知