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

    重来重来,刚刚就当什么都没发生

    今天的题属实有些迷惑,各种意义上…总之都很有难度吧。不满归不满,这套题的确不是什么没有意义的题目。

    为了考验自己的学习能力记忆力,决定不写题解,扔个代码完事了

    其实是懒得写一大堆式子的推理以及想表示一下对出题人的敬意

    你就不怕你到时候回来看一脸懵逼吗

    T1:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int t,mod=998244353,mod1=998244351;
    long long n;
    int main()
    {
        scanf("%d",&t);
        long long num=1,x=9;
        while(mod1){
            if(mod1&1)num=num*x%mod;
            x=x*x%mod;
            mod1>>=1;
        }
        while(t--){
            scanf("%lld",&n);
            n%=mod;
            printf("%lld
    ",(n*n%mod-1)*num%mod);
        }
        return 0;
    }
    View Code

    T2:

    这题考场上出70分思路挺快的,大概20min或者更少,连带读完三道题的时间…这个思路本质上也是推导正解的中间过程

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=1e5+10;
    int n,len,nxt[4*N],lons;
    long long ans,num1[4*N],num2[4*N];
    char s[2*N],c[2*N],a[4*N],ss[4*N];
    void kmp1(int lon){
        strcpy(a+lon+2,s+1);
        a[lon+1]='.';
        int lens=strlen(a+1);
        for(int i=2;i<=lens;i++)nxt[i]=0;
        for(int i=2;i<=lens;i++){
            int j=nxt[i-1];
            while(j&&a[j+1]!=a[i])j=nxt[j];
            if(a[j+1]==a[i])nxt[i]=j+1;
            if(nxt[i]==lon)num1[i-2*lon]++;
        }
    }
    void kmp2(int lon){
        strcpy(a+lon+2,s+1);
        a[lon+1]='.';
        int lens=strlen(a+1);
        for(int i=2;i<=lens;i++)nxt[i]=0;
        for(int i=2;i<=lens;i++){
            int j=nxt[i-1];
            while(j&&a[j+1]!=a[i])j=nxt[j];
            if(a[j+1]==a[i])nxt[i]=j+1;
            if(nxt[i]==lon)num2[i-lon-1]++;
        }
    }
    int main()
    {
        scanf("%s",s+1);
        lons=strlen(s+1);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",c+1);
            len=strlen(c+1);
            for(int j=1;j<=len;j++){
                strcpy(a+1,c+1);
                strcpy(a+j+1,ss+1);
                kmp1(j);
                strcpy(a+1,c+j);
                strcpy(a+len-j+2,ss+1);
                kmp2(len-j+1);
            }
        }
        for(int i=2;i<=lons;i++){
            ans+=num1[i]*num2[i-1];
        }
        printf("%lld
    ",ans);
        return 0;
     } 
    考场70pts

    这个70分的做法是,读入一个字符串以后,暴力地扫出它的所有前后缀,对每一个前后缀在s串上跑一下kmp。如果是后缀,就在s串匹配成功的位置让计数器cnt2++。如果是前缀,就在s串匹配成功的子串的开始位置让计数器cnt1++。

    最后扫一遍计数器数组(到s串的长度),ans+=cnt2i*cnt1i+1

    应该是正解里最基础的那部分吧。

    能想到正解的一部分对于一个菜鸡来说太不容易了赶紧详细写一下【?】

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=1e5+10,p=1500007,mod=998244353;
    int n,len,cnt1=1,cnt2=1,lens;
    char s[2*N],c[2*N];
    int tree1[2*N][27],tree2[2*N][27];
    unsigned long long hash1[2*N],hash2[2*N],ks[2*N];
    long long ans;                                   
    struct node{
        int m;
        #define m 1500007
        int head[2][m+10],Next[2][2*N],tot[2];
        long long siz[2][2*N];
        unsigned long long ver[2][2*N];
        void ins(unsigned long long has,int opt,long long sum){
            unsigned long long x=has%m;
            for(int i=head[opt][x];i;i=Next[opt][i]){
                if(has==ver[opt][i]){
                    siz[opt][i]+=sum;
                    return;
                }
            }
            ver[opt][++tot[opt]]=has,siz[opt][tot[opt]]=sum,Next[opt][tot[opt]]=head[opt][x],head[opt][x]=tot[opt];
            return;
        }
        long long get(unsigned long long has,int opt){
            unsigned long long x=has%m;
            for(int i=head[opt][x];i;i=Next[opt][i]){
                if(has==ver[opt][i]){
                    return siz[opt][i];
                }
            }
            return 0;
        }
    }h;
    void insert(){
        int now=1;
        unsigned long long has=0;
        for(int i=1;i<=len;i++){//前缀 
            has=has*p+c[i];
            if(!tree1[now][c[i]-'a'])tree1[now][c[i]-'a']=++cnt1;
            h.ins(has,0,1);
            now=tree1[now][c[i]-'a'];
        }
        now=1,has=0;
        for(int i=len;i>=1;i--){//后缀 
            has=has*p+c[i];
            if(!tree2[now][c[i]-'a'])tree2[now][c[i]-'a']=++cnt2;
            h.ins(has,1,1);
            now=tree2[now][c[i]-'a'];
        }
    }
    void dfs1(int now,unsigned long long has){
        for(int i=0;i<26;i++){
            if(tree1[now][i]){
                long long val=has*p+i+'a';
                long long sum=h.get(has,0);
                h.ins(val,0,sum);
                dfs1(tree1[now][i],val);
            }
        }
    }
    void dfs2(int now,unsigned long long has){
        for(int i=0;i<26;i++){
            if(tree2[now][i]){
                long long val=has*p+i+'a';
                long long sum=h.get(has,1);
                h.ins(val,1,sum);
                dfs2(tree2[now][i],val);
            }
        }
    }
    long long work(int x){
        int l=1,r=x,ans1=0,ans2=0;
        while(l<=r){//后缀 
            int mid=(l+r)/2;
            unsigned long long val=hash2[mid]-hash2[x+1]*ks[x-mid+1];
            long long sum=h.get(val,1);                             
            if(sum){
                ans2=sum;
                r=mid-1; 
            }
            else l=mid+1;
        }
        l=x+1,r=lens;
        while(l<=r){//后缀 
            int mid=(l+r)/2;
            unsigned long long val=hash1[mid]-hash1[x]*ks[mid-x];
            long long sum=h.get(val,0);                         
            if(sum){
                ans1=sum;
                l=mid+1;
            }
            else r=mid-1;
        }
        return 1ll*ans1*ans2;
    }
    int main()
    {
        scanf("%s",s+1);
        lens=strlen(s+1);
        ks[0]=1;
        for(int i=1;i<=lens;i++){
            hash1[i]=hash1[i-1]*p+s[i];
            ks[i]=ks[i-1]*p;          
        }               
        for(int i=lens;i>=1;i--){
            hash2[i]=hash2[i+1]*p+s[i];
        }                          
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",c+1);
            len=strlen(c+1);
            insert();
        }
        dfs1(1,0);
        dfs2(1,0);
        for(int i=1;i<lens;i++){
            ans+=work(i);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    很神奇的思路转化:让一个中间点前面衔接的最长后缀代表所有更靠近中间点的后缀,记录一个前缀和。因为不用考虑每个前后缀具体的位置,只要存在包含它的更长前后缀就一定能同时对答案产生贡献,所以可以利用trie树来统计一下前缀和。就trie树的操作过程来说,的确非常适合统计数量的前缀和信息。用hash映射所有存在的前后缀,在每个中间点记录答案的时候二分这个最长前后缀。相当于用hash和trie树替代掉了原本一次次跑kmp匹配的过程,非常优秀。

    关于我为什么这么生气的原因,这题在hash上搞幺蛾子?嗯?

    大概率是我菜吧

    记一下要点:巩固一下ull自然溢出,以及哈希表

    哈希表本质是利用关键字的分类(散列函数映射)来加速查询

    T3:

    #include<iostream>
    #include<cstdio>
    #define ll long long
    using namespace std;
    const int mod=3e5+7;
    int t;
    ll n,m,k,ans,rec[mod+10],inv[mod+10],minn;
    ll ksm(ll x,int k){
        ll num=1;
        while(k){
            if(k&1)num=num*x%mod;
            x=x*x%mod;
            k>>=1;
        }
        return num;
    }
    void work(){
        inv[0]=rec[0]=rec[1]=1;
        for(int i=2;i<=mod-1;i++)rec[i]=rec[i-1]*i%mod;
        inv[mod-1]=ksm(rec[mod-1],mod-2);
        for(int i=mod-2;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
    }
    ll cal(ll x,ll y){
        if(x<y)return 0;
        if(!y||!x)return 1;
        return rec[x]*inv[y]%mod*inv[x-y]%mod;
    }
    ll C(ll x,ll y){
        if(x<y)return 0;
        if(!y)return 1;
        return C(x/mod,y/mod)*cal(x%mod,y%mod)%mod;
    }
    int main()
    {
        scanf("%d",&t);
        work();
        while(t--){
            scanf("%lld%lld%lld",&n,&m,&k);
            if(k==1){
                n%=mod,m%=mod;
                printf("%lld
    ",n*m%mod);
                continue;
            }
            minn=min(n,m);
            ans=0;
            if(n>m)swap(n,m);
            ans=(ans+n%mod*C(m,k)%mod+m%mod*C(n,k))%mod;
            ans=(ans+2*(2*C(n,k+1)%mod+(m-n+1)%mod*C(n,k))%mod)%mod;
            if(k==5){
                minn%=mod;
                long long n1=n,m1=m;
                n%=mod,m%=mod;
                minn=((min(n1,m1)-1)/2)%mod;
                ans=(ans+2*(minn*n%mod*m%mod-2*(1+minn)%mod*minn%mod*inv[2]%mod*(m+n)%mod+4*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
            }
            else if(k==4){
                minn%=mod;
                long long n1=n,m1=m;
                n%=mod,m%=mod;
                ans=(ans+(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+n)%mod+minn*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
                minn=min(m1/2,n1)%mod;
                ans=(ans+2*(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+2*n)%mod+2*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
                swap(n1,m1);
                swap(n,m);
                minn=min(m1/2,n1)%mod;
                ans=(ans+2*(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+2*n)%mod+2*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
                minn=((min(n1,m1)-1)/2)%mod;
                ans=(ans+5*(minn*n%mod*m%mod-2*(1+minn)%mod*minn%mod*inv[2]%mod*(m+n)%mod+4*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
            }
            else if(k==3){
                minn%=mod;
                long long n1=n,m1=m;
                n%=mod,m%=mod;
                ans=(ans+4*(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+n)%mod+minn*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
                minn=min(m1/2,n1)%mod;
                ans=(ans+2*(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+2*n)%mod+2*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
                swap(n1,m1);
                swap(n,m);
                minn=min(m1/2,n1)%mod;
                ans=(ans+2*(minn*n%mod*m%mod-(1+minn)*minn%mod*inv[2]%mod*(m+2*n)%mod+2*minn%mod*(minn+1)%mod*(2*minn+1)%mod*ksm(6,mod-2)%mod+mod)%mod)%mod;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /*
    3
    7 7 3
    7 6 3
    8 9 3
    
    1
    5 5 1
    */
    View Code

    记一下平方和公式:

    差点忘了这个:

    朱世杰恒等式

    其实它和平方和公式也有点联系

  • 相关阅读:
    学好VC++的十大良好习惯
    VC6.0调试技巧
    匈牙利命名法
    VC中常用文件操作(三)
    VL_FEAT——图像处理函数库
    MATLAB——linspace
    (Your)((Term)((Project)))(字符串处理)
    Matrix Chain Multiplication(栈的简单应用)
    The Triangle(DP基础)
    MATLAB——polyfit
  • 原文地址:https://www.cnblogs.com/chloris/p/11719531.html
Copyright © 2011-2022 走看看