zoukankan      html  css  js  c++  java
  • 20190822考试

    期望:80 + 30 + 10 = 120.实际: 50 + 30 + 10 = 90.

    T1:小P写出二叉树的中序遍历,但他没判断左右儿子是否存在就遍历,我们设访问到虚点时输出'#',给定数字(存在点)与'#'(虚点),要求你判断是否存在这样的二叉树?

    S1:蒟蒻理解题意错了,以为只要是给定的条件能组成二叉树即可(条件给多少个虚点就访问多少个虚点),立马想到了区间DP,类似于加分二叉树的做法.设f[ i ][ j ]表示区间[ i , j ]区间能否组成二叉树,f[ i ][ j ]唯一为true的情况为f[ i ][ k -1 ] = true,f[ k + 1 ][ j ] = true,s[ k ][ 0 ] ! =' # ',同时注意枚举的根为i/j的情况,还想了一个剪枝,就是我们判断f[ i ][ j ]为true后,只要找到中间点 k = i-1,对称区间 j1 = i -2,i1 = j1 -len + 1.只要f[ i1 ][ j1 ]&&f[ i ][ j ]&&s[ k ][ 0 ] ! = '#',我们就就能判断f[ i1 ][ j ]区间合法.这样我们就能O(n^3)过1000的点(80分),但测出来前3个点WA了,与dalao交流后发现自己理解错了,可能存在虚点就一定要访问,并不是给定多少个就访问多少个就可以了.那么唯一合法形式就为"# 数字 # 数字 # 数字 # ......",直接判断即可,注意'#'数量小于数字数量+1的情况.

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define re register
    int T,n;
    string s[100010];
    int main()
    {
        freopen("traversal.in","r",stdin);
        freopen("traversal.out","w",stdout);
        scanf("%d",&T);
        while(T--){
            int flag=0,ans=1,cnt=0,sur=0;
            scanf("%d",&n);
            for(re int i=1;i<=n;++i){
                cin>>s[i];
                if(s[i][0]=='#')
                    ++cnt;
                else ++sur;
            }
            if(cnt!=sur+1){
                printf("No
    ");
                continue;
            }
            for(re int i=1;i<=n;++i){
                if(i==1){
                    if(s[i][0]=='#')
                        flag=1;
                    else{
                        ans=0;
                        break;
                    }
                }
                else{
                    if((flag&&s[i][0]=='#')||(!flag&&s[i][0]!='#')){
                        ans=0;
                        break;
                    }
                    if(flag)
                        flag=0;
                    else flag=1;
                }
            }
            if(ans)
                printf("Yes
    ");
            else printf("No
    ");
        }
        return 0;
    }

    T2:小 P 最近迷上了一款选数游戏.游戏一开始会给定 b 组数字序列,每一组序列都包含相同的n 个元素,每个元素都在 1 到 9 以内.小 P 必须从每组数字序列中都挑选一个数字,按顺序组合成一个大整数.例如如果小 P 挑选 1 和 2,那么他就得到了整数 12.之后,小 P 需要计算这个大整数除以 x 的余数。如果这个余数正好等于 k,那么小 P 就取得了游戏的胜利.现在小 P 想知道,一共有多少种胜利的方式.两种方式被视为不同,当且仅当至少在一组序列中,小 P 挑选了不同位置上的数字.由于答案可能很大,你只要对1000000007取模后的值即可.

    S2:考试中只写了30分的爆搜.对于60分,我们考虑dp.先预处理一个cnt[ i ][ j ]表示从%x余数为i变化到%x余数为j的方案数,我们枚举余数[ 0 , x -1 ],在枚举[ 1 , n ]区间的val[ k ] ,那么转换的余数 j = ( i * 10 + val[ k ] ) %x , cnt[ i ][ j ]+1.为什么i要乘10?看一个例子,1546 % x = ( ( ( ( 1 %x ) * 10  + 5) % x) *10 + 4) %x)*10 + 6 )%x,所以我们就前一位数的余数变为加一位数后的余数要乘10再取摸.我们设f[ k ][ i ]表示第 k次选择,得到余数为i的次数.有转移:f[ k ][  i  ] + = f[ k -1 ][ j ]*cnt[ j ][ i ].(k为第几次选,i,j为余数).初始化f[ 0 ][ 0 ] = 1,蒟蒻想这个初始化为什么有意义?因为开始蒟蒻是预处理 k =1 的f[ 1 ][ i ]的情况,从k = 2 开始转移的.也就是让f[ 1 ][ i ]从f[ 0 ][ 0 ]的到转移,f[ 1 ][ i ] + = f[ 0 ][ 0 ]*cnt[ 0 ][ i ],发现此时的cnt[ 0 ][ i ]为i = ( 0*10+val[ k ] )%x = ( val[ k ] ) %x.就为f[ 1 ][ i ]的预处理状态.60分code:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define re register
    const int maxn=500010;
    const int mod=1000000007;
    int n,b,k,x,val[maxn],cnt[101][101],f[10001][101];
    int main()
    {    
        freopen("number.in","r",stdin);
        freopen("number.out","w",stdout);
        scanf("%d%d%d%d",&n,&b,&k,&x);
        for(re int i=1;i<=n;++i)
            scanf("%d",&val[i]);
        for(re int i=0;i<=x-1;++i)
            for(re int j=1;j<=n;++j){
                int p=i,q=(p*10+val[j])%x;
                ++cnt[p][q];
            }
        f[0][0]=1;
        for(re int g=1;g<=b;++g)
            for(re int i=0;i<=x-1;++i)
                for(re int j=0;j<=x-1;++j)
                    f[g][i]=(f[g][i]%mod+1ll*f[g-1][j]*cnt[j][i]%mod)%mod;
        printf("%d",f[b][k]);
        return 0;
    }

    但b高达1e9,这样显然超时.我们发现f[ g ][ i ]由f[ g -1 ][  j ]转移,g状态由g - 1的转态,我们考虑将转移过程放入矩阵转移中,设初始矩阵f[ 0 ][ 0...x-1 ](注意只有f[ 0 ][ 0]有意义),转化矩阵为cnt[ i ][  j ],我们对cnt[ i ][ j ]进行b次矩乘,矩乘如何替代dp的转移?矩乘是行与列相乘求和,例如x = 4时,f[ 1 ][ 3 ]为Σf[ 1 ][ 1 ]*f[ 1 ][ 3 ] + f[ 1 ][ 2 ]*f[ 2 ][ 3]+f[ 1 ][ 3 ]*f[ 3 ][ 3 ],显然矩乘性质替代了dp的第二层的枚举.最后输出f[ 0 ][ k ]就是初始矩阵与转移矩阵在矩乘,只有f[ 0 ][ 0 ]等于1,那么ans就为f[ 0 ][ k ].

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    #define re register
    #define LL long long
    const int maxn=500010;
    const int mod=1000000007;
    long long n,b,k,x,ans,val[maxn];
    struct chang{
        LL cnt[101][101];
        chang(){memset(cnt,0,sizeof(cnt));}
        chang operator*(const chang p){
            chang ans;
            for(re LL g=0;g<x;++g)
                for(re LL i=0;i<x;++i)
                    for(re LL j=0;j<x;++j){
                        ans.cnt[i][j]=(ans.cnt[i][j]%mod+(1ll*cnt[i][g]*p.cnt[g][j])%mod)%mod;
            }
            return ans;
        }
    };
    chang qsm(chang a,int b)
    {
        chang ans;
        for(re LL i=0;i<x;++i)
            ans.cnt[i][i]=1;
        while(b){
            if(b&1) 
                ans=ans*a;
            a=a*a;
            b>>=1;
        }
        return ans;
    }
    int main()
    {
        freopen("number.in","r",stdin);
        freopen("number.out","w",stdout);
        scanf("%lld%lld%lld%lld",&n,&b,&k,&x);
        for(re LL i=1;i<=n;++i)
            scanf("%lld",&val[i]);
        chang a;
        for(re LL i=0;i<x;++i)
            for(re LL g=1;g<=n;++g){
                LL j=(10*i+val[g])%x;
                ++a.cnt[i][j];
            }
        a=qsm(a,b);
        printf("%lld",a.cnt[0][k]);
        return 0;
    }

    T3:2075 年,太阳即将毁灭,地球已经不适合人类生存,而面对绝境,人类将开启“流浪地球”计划,试图带着地球一起逃离太阳系,寻找人类新家园。为了获得对推动地球前进的动力,人们发明了一种超高能粒子发射装置。发射装置一共有 n 个节点,节点编号为 1–n,由 n − 1 条等长的粒子加速通道联通,加速通道是双向的,且任意两个节点之间可以互相到达。每个加速通道的长度都是 1。但不同加速通道能加速的粒子质量是不同的,其中第 i 条加速通道只能加速质量小于等于 a i 的粒子,其余粒子可以通过,但无法获得加速效果。粒子连续通过不同单位长度的加速可以获得不同的能量,甚至可能获得负能量。粒子最终的总能量是多次连续加速获得的能量之和。科学家们为了测试发射装置的性能,准备了 q 次试验,一次试验可以被如下描述:u, v, l:从 u 点发射一个质量为 l 的粒子,该粒子最终从 v 点射出。粒子的初始能量为 0,且粒子只走最短路。现在,你作为本次实验的执行总管,你需要提交每次实验粒子的最终总能量。由于装置耗能巨大,你需要在有限时间内完成实验.

    ①:对于①②点,我们选择求最短路,同时用前驱数组记录路径,按照定义求ans.复杂度:O((n+n+1)log(n+1))*q.

    ②:对于第③点,我们直接判断l,如果l>质量限制,ans=0.如果l<=质量限制,直接用dis[ s ]+dis[ t ]-s*dis[ lca(s,t)]求出树上两点距离,在找出距离对应的ans.O(nlogn+qlogn).

    ③:对于第④点,考虑求出两点的lca,将路劲划分为s--->lca,t--->lca.每次只走father,这样我们用O(路径长)求出路劲长,再求ans,期望复杂度可以卡过去,考虑s,t就为lca情况.

    ④:对于第⑤⑥点,对于粒子质量为0直接求链长再求ans.对于粒子质量为1,考虑用线段树维护一个区间1的个数,再考虑预处理每一个有连续1的区间[ L,R ],同时对于每个区间id  处理出sum[ i ]表示前i个区间的长度,显然[ L,R ]单调递增,考虑对于查询的区间[L,R],我们二分查找第一个大于等于L的id1与第一个小于等于R的id2,这样我们得到sum[id2]-sum[id1-1]为这段区间连续1的个数,再用线段树求出的1的总数-连续1的个数=不连续1的个数,最后剩下的就为0的个数.根据定义求出ans即可.

    ⑤:对于第10.11点,考虑将③与④的结合,用③求出树上路径,用④方法求出大于l的连续与不连续个数,应为随机数据,期望复杂度能过.

    这样我们愉快地拿下40pts,暴力选手告辞.


     

  • 相关阅读:
    2016"百度之星"
    codeforces 55 div2 C.Title 模拟
    codeforces 98 div2 C.History 水题
    codeforces 97 div2 C.Replacement 水题
    codeforces 200 div2 C. Rational Resistance 思路题
    bzoj 2226 LCMSum 欧拉函数
    hdu 1163 九余数定理
    51nod 1225 余数的和 数学
    bzoj 2818 gcd 线性欧拉函数
    Codeforces Round #332 (Div. 2)D. Spongebob and Squares 数学
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11398653.html
Copyright © 2011-2022 走看看