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\n",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("\n");
    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++。

posted @ 2019-09-19 18:13  Chloris_Black  阅读(205)  评论(0编辑  收藏  举报