2018年西安电子科技大学程序设计新生赛网络赛题解

2019-04-14 16:02发布

好久没发博客了,之前虽然也在做题,不过做出来了之后总是懒,就一直没发。。。 上周新生赛的网络赛就开始了,不过由于上周事情有些多,就一直没有打这场比赛。昨天把题都补了一下。。。 这次新生赛网络赛难度不是特别大,因为面向的群体主要是新生。个人看来,大约有4道签到题,分别是A,B,C,E题。 4道简单题,D,F(如果这道题掉进树dp的坑就不简单了),G,H。I,J题难度大些,算是这次比赛的难题。下面是这次比赛每道题的题解。 --------------------------------------------------------------------------------------------------------------------------------------------------------------- A题   Fibonacci问题 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=0 这题纯粹是一个签到题,就不说了 代码: #include using namespace std; typedef long long ll; ll f[45]; int main() { int t,n; scanf("%d",&t); f[0]=1; f[1]=1; for(int i=1;i<=40;i++) f[i]=f[i-1]+f[i-2]; while(t--) { scanf("%d",&n); printf("%lld ",f[n]); } return 0; } B题  韦神爱跑步 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=1 看似很吓人的一道题,既然放在第二题的位置自有他的深意。稍微找一下规律就可以发现所求值是最大的区间的长度 代码: #include using namespace std; typedef long long ll; int main() { int t; ll s,e; while(scanf("%d",&t)!=EOF) { ll res=0; for(int i=1;i<=t;i++) { scanf("%lld%lld",&s,&e); res=max(res,(e-s)+1); } cout << res*res << endl; } return 0; } C题  Special Judge 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=2 这题其实1,3判断都极其简单,判断B是A的子序列的话O(n)就可以了,具体方法是一个位置指针指向A数组,for循环遍历B数组的元素,既然是子序列,那么A的指针是一直向前的,当指针移到A的末尾后再看是否还有B中元素没有找就可以了。 代码: #include using namespace std; typedef long long ll; ll lena,lenb,k; ll a[1010]; ll b[1010]; bool judge1() { if(lenb<=2) return true; for(int i=3;i<=lenb;i++) { if(b[i]!=b[i-1]+b[i-2]) return false; } return true; } bool judge2() { int p=0; for(int i=1;i<=lenb;i++) { while(p<=lena) { p++; if(a[p]==b[i]) break; } } if(p>lena) return false; return true; } int main() { while(scanf("%lld%lld%lld",&lena,&lenb,&k)!=EOF) { for(int i=1;i<=lena;i++) scanf("%lld",&a[i]); for(int i=1;i<=lenb;i++) scanf("%lld",&b[i]); if(!judge1()) printf("Wrong Answer - The participant's answer does not obey the recursion "); else { if(!judge2()) printf("Wrong Answer - The participant's answer is not a subsequence of input "); else { if(lenb D题  火聚聚的排列 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=3 这次自己动手画一画,推一推就可以发现结论是2^n-2,别忘了取模就行 代码: #include using namespace std; typedef long long ll; void scan(__int128 &x) { x = 0; int f = 1; char ch; if((ch = getchar()) == '-') f = -f; else x = x*10 + ch-'0'; while((ch = getchar()) >= '0' && ch <= '9') x = x*10 + ch-'0'; x *= f; } void print(__int128 x) { if(x < 0) { x = -x; putchar('-'); } if(x > 9) print(x/10); putchar(x%10 + '0'); } __int128 powerMod(__int128 x,__int128 n,__int128 m) { __int128 res=1; while(n>0) { if(n&1) res=(res*x)%m; x=(x*x)%m; n>>=1; } return res; } int main() { int t; ll n,p; int cnt=0; scanf("%d",&t); while(t--) { scanf("%lld%lld",&n,&p); cnt++; if(n==1) { printf("Case #%d: %lld ",cnt,1%p); } else { printf("Case #%d: ",cnt); print((powerMod(2,n,p)-2+p)%p); printf(" "); } } return 0; } E题   ICPC式家长 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=4 遇到这种简单的博弈论问题,直接莽结论即可。我做的时候推到了5,就不推之后的了,除了0外,其他的答案都是一样的。。。 代码: #include using namespace std; int main() { int t,n; scanf("%d",&t); for(int i=1;i<=t;i++) { scanf("%d",&n); if(n==0) puts("The wisdom tree master No.1"); else puts("Greenty_Q"); } return 0; } F题  果果的树 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=5 这题如果是往dp上搞的话,就有些难了(主要是我太菜,树dp搞不来) 正解是黑白染 {MOD},一遍dfs,将整个树染成相邻的节点不同 {MOD}的树。路径长度为奇数就是黑白两 {MOD}节点之间的边。如果黑 {MOD}节点共a个,白 {MOD}节点共b个。那么答案就是a*b。 代码: #include using namespace std; typedef long long ll; vector g[100010]; int vis[100010]; ll cnt[2]; int x,y; void dfs(int cur,int col) { cnt[col]++; vis[cur]=1; for(int i=0;i G题   tsy的数 II 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=6 拿道题目可以先试一下小的数,发现f(4)=f(2),f(8)=f(4)=f(2),进而可以得到,f(2^k)=f(2)=2,同理可得f(3^k)=f(3)=3,总结一下即为 f(p^k)=p,p为素数。而两个素数之间的gcd为1,又知任何一个合数可表示成(2^x1)*(3^x2)*(5^x3)*......,即若干素数的幂次积。 所以此题到这里也就很明显了,就是把合数分解,把构成合数的素数乘起来就行了。(注意:同一个素数只乘一次) 代码: #include using namespace std; typedef long long ll; const int MAXN = 10000; int prime[MAXN + 1]; void getPrime() { memset(prime, 0, sizeof(prime)); for (int i = 2; i <= MAXN; i++) { if (!prime[i]) { prime[++prime[0]] = i; } for (int j = 1; j <= prime[0] && prime[j] <= MAXN / i; j++) { prime[prime[j] * i] = 1; if (i % prime[j] == 0) { break; } } } return ; } long long factor[100][2]; int fatCnt; int getFactors(long long x) { fatCnt = 0; long long tmp = x; for (int i = 1; prime[i] <= tmp / prime[i]; i++) { factor[fatCnt][1] = 0; if (tmp % prime[i] == 0) { factor[fatCnt][0] = prime[i]; while (tmp % prime[i] == 0) { factor[fatCnt][1]++; tmp /= prime[i]; } fatCnt++; } } if (tmp != 1) { factor[fatCnt][0] = tmp; factor[fatCnt++][1] = 1; } return fatCnt; } int main() { ll n; getPrime(); while(scanf("%lld",&n)!=EOF) { int len=getFactors(n); ll sum=1; for(int i=0;i H题   火聚聚的music list 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=7 二维树状数组的裸题,直接秒了。。。 代码: #include using namespace std; int n,m,h; int tree[1010][1010]; void add(int x, int y, int z){ int memo_y = y; while(x <= n){ y = memo_y; while(y <= m) tree[x][y] += z, y += y & -y; x += x & -x; } } void range_add(int xa, int ya, int xb, int yb, int z){ add(xa, ya, z); add(xa, yb + 1, -z); add(xb + 1, ya, -z); add(xb + 1, yb + 1, z); } int ask(int x, int y){ int res = 0, memo_y = y; while(x){ y = memo_y; while(y) res += tree[x][y], y -= y & -y; x -= x & -x; } return res; } int main() { scanf("%d%d%d",&n,&m,&h); int x,y; for(int i=1;i<=h;i++) { scanf("%d%d",&x,&y); range_add(1,1,x,y,1); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%d ",ask(i,j)); } return 0; } I题   Ar与妹子 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=8 最开始直接模拟做了,TLE。。。 仔细一看,n为1e5。其实这道题用了二叉搜索树的一些性质。二叉搜索树的中序遍历序列是递增序列,那么当前元素在中序遍历中的前驱节点和后继节点中晚出现的那么就是他的父亲,由于set自带排序功能,所以用set搞一下会比较好。 (这题我加了很多特判,代码写的巨丑。。。还是别看了,其实直接在set中插入一个首尾元素就不必特判了) 代码: #include using namespace std; int a[100010]; int ti[100010]; int res[100010]; set s; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); ti[a[i]]=i; s.insert(a[i]); set::iterator itt=s.end(); set::iterator itt2=s.begin(); itt--; if(s.size()==1) { res[a[i]]=0; } else if(s.size()==2) { s.erase(a[i]); res[a[i]]=*s.begin(); s.insert(a[i]); } else if(s.find(a[i])==itt) { set::iterator it=s.find(a[i]); set::iterator it1=it; it1--; int l=*(it1); res[a[i]]=l; } else if(s.find(a[i])==itt2) { set::iterator it=s.find(a[i]); set::iterator it1=it; it1++; int l=*(it1); res[a[i]]=l; } else { set::iterator it=s.find(a[i]); set::iterator it1=it; set::iterator it2=it; it1--,it2++; int l=*(it1); int r=*(it2); if(ti[l]>ti[r]) res[a[i]]=l; else res[a[i]]=r; } } printf("%d",res[1]); for(int i=2;i<=n;i++) printf(" %d",res[i]); printf(" "); return 0; } J题   zxy的射击 题目链接:http://acm.xidian.edu.cn/problem.php?cid=1050&pid=9 此题是本场最难的题,既然题目名称是zxy的射击,那么直接把zxy的题解搬过来就好了(主要是窝太菜了,讲不好 代码: #include using namespace std; typedef long long ll; ll data[1010][1010]; const ll mod=1e9+7; long long powM(long long a, long long b, long long m) { long long tmp = 1; if (b == 0) { return 1; } if (b == 1) { return a % m; } tmp = powM(a, b >> 1, m); tmp = tmp * tmp % m; if (b & 1) { tmp = tmp * a % m; } return tmp; } long long inv(long long a, long long m) { return powM(a, m - 2, m); } void NGetM(int m, int n) { int min, i, j; data[0][0] = 1; for (i = 1; i <= m; i++) { data[i][0] = 0; } for (i = 0; i <= m; i++) { data[i][i + 1] = 0; } for (i = 1; i <= m; i++) { if (i < n) { min = i; } else { min = n; } for (j = 1; j <= min; j++) { data[i][j] = (j * data[i - 1][j] + data[i - 1][j - 1])%mod; } } return ; } int main() { ll n,k,p; while(scanf("%lld%lld%lld",&n,&k,&p)!=EOF) { ll rev=inv(100,mod); ll jie=1; ll sum=0; NGetM(k,k); for(int i=1;i<=k;i++) { sum=(sum+(((data[k][i]*jie)%mod*(n-i+1))%mod*powM((p*rev)%mod,i,mod))%mod)%mod; jie=(jie*(n-i+1))%mod; } printf("%lld ",sum); } return 0; }