20190822考试
Posted xqysckt
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n"); 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\n"); else printf("No\n"); 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,所以我们就前一位数的余数变为加一位数后的余数.我们设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,且粒子只走最短路。现在,你作为本次实验的执行总管,你需要提交每次实验粒子的最终总能量。由于装置耗能巨大,你需要在有限时间内完成实验.
S3:咕咕.
以上是关于20190822考试的主要内容,如果未能解决你的问题,请参考以下文章