zoukankan      html  css  js  c++  java
  • 2019.9.18 csp-s模拟测试46 反思总结

    神志不清:

    回去休息(x)继续考试(√)

    非常爆炸的一次考试。看错题码完T1回去再看发现自己过于幼稚,T2读完题看着16mb的空间秒出正解然后逻辑出现致命失误100pts->0pts,T3看了一会题直接放弃,10pts最后有时间再说->没时间,下一个。

    还有什么好说的呢,略略。

    T1:Set

    曾经学完鸽巢原理【抽屉原理】以后还想过怎么考这个知识点的问题,最后没想出个所以然,断定一定是看不出来考点的考法。然后我真没看出来…一开始还读错题。T2打了个自以为的正解以后看了一会儿T3翻回来检查这道题,推翻了重新想。

    大约想了一个小时?对 非常睿智。之后整个转换了思路才终于想到DP。半小时紧急打个DP然后瞎调一通,居然有50pts。

    最后考完听别人一说才恍然大悟,这不是背包吗…

    正解则完全不一样,是上面所说的鸽巢原理。余数有一个性质,n的余数不超过n种。对于给出的数列取模做个前缀和,从S0到Sn一共有n+1种余数,那么一定存在余数相同的两个S,把这两个位置中间的一段输出,它们的和一定被n整除。

    非常巧妙的一道题。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n;
    int s[1000010],v[1000010];
    int main()
    {
        scanf("%d",&n);
        v[0]=0;
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            s[i]=(s[i-1]+x)%n;
            if(!v[s[i]]&&s[i]!=0)v[s[i]]=i;
            else{
                printf("%d
    ",i-v[s[i]]);
                for(int j=v[s[i]]+1;j<=i;j++)printf("%d ",j);
                return 0;
            }
        }
        printf("-1");
        return 0;
    }
    View Code

    T2:Read

    16mb,读入又很麻烦,显然是一边读入一边处理答案的类型。

    看题发现要先求出众数,再加上边读入边处理,自然想到了摩尔投票法。摩尔投票法还是当时准备给大家讲平衡树的时候在一道题里用到过,我引给大家的算法……结果这次四个人里只有我wa了……

    wa的原因是处理完众数以后,摩尔投票法剩下的那个次数我直接用来求答案了。实际上最后存下的那个“众数”不一定是真正的众数,有可能恰巧是剩下的数字,并不多于所有数的一半。应该再扫一遍看是否大于(n+1)/2,然后大于的话再减去合适的数量。(n+1)/2和之后的那个数量都可以列出式子来移项得到。

    足以说明我当时多么睿智…虽然现在也好不到哪去。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    long long X,Y;
    int sum,cnt,m,k,count[1010],x[1010],y[1010],z[1010];
    int main()
    {
        scanf("%d%d",&m,&k);
        for(int i=1;i<=m;i++)scanf("%d",&count[i]);
        for(int i=1;i<=m;i++)scanf("%d",&x[i]);
        for(int i=1;i<=m;i++)scanf("%d",&y[i]);
        for(int i=1;i<=m;i++)scanf("%d",&z[i]);
        int N=0,S=(1<<k)-1;
        for(int i=1;i<=m;i++){
            X=x[i];
            if(X==Y)cnt++;
            else{
                cnt--;
                if(cnt<0){
                    cnt=1;
                    Y=X;
                }
            }
            long long lst=x[i];
    //        printf("%d ",X);
            for(int j=1;j<count[i];j++){
                lst=(lst*y[i]+z[i])&S;
                X=lst;
                if(X==Y)cnt++;
                else{
                    cnt--;
                    if(cnt<0){
                        cnt=1;
                        Y=X;
                    }
                }
    //            printf("%d ",X);
            }
        }
        for(int i=1;i<=m;i++){
            N++;
            X=x[i];
            if(X==Y)sum++;
            long long lst=x[i];
    //        printf("%d ",X);
            for(int j=1;j<count[i];j++){
                lst=(lst*y[i]+z[i])&S;
                N++;
                X=lst;
                if(X==Y)sum++;
    //            printf("%d ",X);
            }
        }
        if(sum>(N+1)/2)printf("%d",sum*2-N-1);
    //    printf("
    ");
        else printf("0");
        return 0;
    }
    View Code

    T3:Race

    看了一眼题,想了会儿,直接扔了。

    最后看正解果然是我的思路盲区…

    x2的实质:比当前选手排名高的人任取两个组成的有序数对的个数,数对中的两个数可以重复。

    在2m天中同样的数对可能多次出现,那么可以考虑这个数对出现了多少次,进一步转化成每个数对于同一选手的贡献。

    对于一位选手,因为所有人的能力值均不相同,其他人一定可以根据能力值二进制第一位和他不同的位数分成至多m组。可以用01trie数记录下所有能力值并存一个size,对于每个运动员都在树上查一次,记录下第一位和他不同的数的个数。每个与他不同的数,排名大于他的次数都是总次数的二分之一,那么一个有序数对就是四分之一即2m-2次*2【因为数对有序所以*2】。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,m;
    long long ans;
    const long long mod=1000000007;
    int a[200010],tree[6000010][2],tot=1,sum[6000010][2],f[31];
    void ins(int x){
        int now=1;
        for(int i=m-1;i>=0;i--){
            if(!tree[now][(x>>i)&1])tree[now][(x>>i)&1]=++tot;
            sum[now][(x>>i)&1]++;
            now=tree[now][(x>>i)&1];
        }
    }
    void work(int x){
        int now=1;
        for(int i=m-1;i>=0;i--){
            f[i]=sum[now][((x>>i)&1)^1];
            now=tree[now][(x>>i)&1];
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            ins(a[i]);
        }
        for(int i=1;i<=n;i++){
            work(a[i]);
            long long num=0;
            for(int j=0;j<m;j++){
                for(int k=j;k<m;k++){
                    num=(num+(2ll*f[j]*f[k]%mod*(1<<(m-2))%mod))%mod;
                }
            }
            ans^=num;
        }
        printf("%lld",ans);
        return 0;
    }
    View Code

    马上考试,祝大家rp++。

  • 相关阅读:
    用户交互语句
    基础数据类型补充与总结
    Python 中表示 False 的方法
    集合
    字典
    元组
    列表
    整型数据详述和进制转换
    f-strings 详解
    字符串方法详解
  • 原文地址:https://www.cnblogs.com/chloris/p/11551553.html
Copyright © 2011-2022 走看看