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,暴力选手告辞.


     

  • 相关阅读:
    4.函数
    3.文件操作及编码解码补充
    2.列表字符串字典元组集合
    1.杂项三元运算及字符编码
    python-数据类型补充及文件处理操作
    python-day02数据类型-字符串和列表的操作
    python-day01
    DOM
    javascript基本
    CSS几个属性
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11398653.html
Copyright © 2011-2022 走看看