2014苏州大学新生赛11月新生赛简单题解
比赛场链接:http://acm.hdu.edu.cn/diy/contest_show.php?cid=25601
对于本次新生赛感觉还是挺满意的(萌萌哒),题目挺好(除了个别坑),榜单挺好看,我们的新生做的和我预期差不多,希望大家能再接再厉,无论好与不好,继续加油!不废话啦,题解写上:
前四道题目是通神出的
1.直接输出(A+B)*32767)%C
一个是需要long long,另一个是16位有符号整数能表示的最大值是32767有人不知道,有人说这个题目有什么意思,其实就是想考你这个计算机基础知识。
2.考虑到负数是((A-B)%C+C)%C
题目没有标注负数情况是有些坑,但即使说了负数有些人也不知道这个同余公式。
3.答案((A%C)*(B%C))%C
这题按通神原本意思不应该是这样的,结果C小了,long long给水过去了,这样就和第二题取模运算的知识点重复了。
4.通神的防AK题
验题时我感觉不会,直接忽略这道题目,现在想想其实挺简单的,要注意到a的范围是9以内,就是大胆的数组模拟大数*i,然后结果每个保存在数组里面,看了代码就能理解了。
1 #include <cstdio> 2 #include <cstring> 3 int ans[4005],cnt[10][4005]; 4 int main(){ 5 int a,b,i,j,add,kkk=1,T; 6 memset(cnt,0,sizeof(cnt)); 7 for(b=1;b<=4000;b++)cnt[1][b]=1; 8 for(a=2;a<=9;a++){ 9 memset(ans,0,sizeof(ans)); 10 ans[1]=a; 11 cnt[a][1]=a; 12 ans[0]=1; 13 for(b=2;b<=4000;b++){ 14 i=1;add=0; 15 for(i=1;i<=ans[0];i++){ 16 ans[i]=ans[i]*a+add; 17 add=ans[i]/10; 18 ans[i]=ans[i]%10; 19 } 20 if(add){ 21 ans[0]++; 22 ans[ans[0]]=add; 23 } 24 for(j=1;j<=i+2;j++)cnt[a][b]+=ans[j]; 25 } 26 } 27 scanf("%d",&T); 28 for(i=1;i<=T;i++){ 29 scanf("%d%d",&a,&b); 30 printf("Case #%d: %d\n",i,cnt[a][b]); 31 } 32 return 0; 33 }
5.答案是a,b最大值,无坑良心水题,要是把我的题目放前面估计榜单会好看一些。
6.这个题目是自己之前看到过的改变而来,有些同学以为答案就是log2(x),为了让大家看到我的良心题无坑,特地把9的样例答案2写上去了。
这样想,我们假设要测n个钻石,1.如果n能被3整除那就均分成3堆,然后随意拿两堆出来测肯定可以得到假钻石在哪一堆,所以问题转化为f[n/3]+1。2.如果n不能被3整除就分成n/3的三堆,然后剩下一个就放在任意一堆,两个就随意找两堆各放一个,这样拿相同的两堆出来就知道假钻石在哪一堆,显然最差也就是在多的那堆,即为f[n/3+1]+1。这是一种做法,其实,最后我们可以发现,答案就是log3(x)向上取整。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int f[210000]; 7 int main() 8 { 9 f[2]=f[3]=1; 10 for(int i=4;i<=100000;i++) 11 { 12 if(i%3==0) f[i]=f[i/3]+1; 13 else f[i]=f[i/3+1]+1; 14 } 15 int n; 16 while(scanf("%d",&n)==1) 17 { 18 printf("%d\n",f[n]); 19 } 20 return 0; 21 }
7.这个博弈其实只要想通了就会觉得很简单。
自己在纸上画一个圈,当A取过1或2个棋子后所有剩余棋子正好形成了一条链。这样B在剩下棋子中选择,如果剩下棋子为奇数B只要拿走中间一个,所有棋子即被拆成两个一模一样的链,接下来无论A怎么取,B在另一堆都能和A取的一样直到B拿走最后一个棋子。如果剩下棋子为偶数B只需要拿走中间两个棋子,这样就又形成了两个相同的链,B和上面一样。所以我们得到结论,除非n是1和2A可以直接取完,否则B总能取到最后一个棋子。
8.为了让大家做好入门经典模拟题,所以出了这道日期相差天数题目,唯一的坑在于两个日期大小不一定。
最简洁的莫过于加一个0年0月0日的新日期,计算两个日期到新日期差,然后答案即为差。
1 #include<cstdio> 2 #include <algorithm> 3 int month[20]={0,31,28,31,30,31,30,31,31,30,31,30,31}; 4 int run(int x){if((x%4==0&&x%100!=0)||x%400==0)return 1;else return 0;} 5 int sum(int x,int y,int z,int ans){ 6 for(int i=0;i<=x-1;i++)if(run(i))ans+=366;else ans+=365; 7 if(run(x))month[2]=29;else month[2]=28; 8 for(int i=1;i<=y-1;i++)ans+=month[i];ans+=z; 9 return ans; 10 } 11 int main(){ 12 int x,y,z,a,b,c; 13 while(scanf("%d%d%d%d%d%d",&x,&y,&z,&a,&b,&c)!=EOF) 14 printf("%d\n",abs(sum(x,y,z,0)-sum(a,b,c,0))); 15 return 0; 16 }
9.这是本场比赛唯一一道算法题了,额毕竟要学习一些新东西。本题需要用到快排+二分,如果不知道这个知识点可以先去百度学学。
首先我们来看k不为0的情况,那么把四个数组都加一个0表示这个数组中这个数不取,这样把A数组每位数+B数组每位数放在一个新的n×n的数组V1里面,sort排序,同理把C数组每位数+D数组每位数放在一个新的V2的数组里面,sort排序,这样对于V1里面的每个数,二分查找V2里面有没有k-V1[i]的数,只要找到一个就是YES,都没找到就是NO。再来看k为0的时候,这个时候我们假设还用上面那个方法,这个时候就出现了一个问题,最终你可能一个数都没取,这样就不符合它的至少有一个人参加要求,所以我们需要另外加了flag变量来判断最终得到的k是否是所有另外加上去的四个0构成的,是就是NO,否则就是YES。具体一些细节的处理和技巧可以看看代码,觉得鹏神的代码更好理解所以把他的代码贴出来。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 7 int n,k; 8 int v[5][2010]; 9 int v1[2010 * 2010]; 10 int v2[2010 * 2010]; 11 int cnt1,cnt2; 12 13 int main(){ 14 while(scanf("%d%d",&n,&k) != EOF){ 15 if(n == 0){ 16 printf("NO\n"); 17 continue; 18 } 19 int flag1 = 0,flag2 = 0; 20 cnt1 = cnt2 = 0; 21 v1[++cnt1] = 0; 22 v2[++cnt2] = 0; 23 for(int i = 1; i <= 4; ++i){ 24 for(int j = 1; j <= n; ++j){ 25 scanf("%d",&v[i][j]); 26 if(v[i][j] == 0){ 27 if(i < 3) flag1 = 1; 28 else flag2 = 1; 29 } 30 } 31 } 32 // v[1] , v[2] 只取其一 33 for(int i = 1; i <= n; ++i){ 34 v1[++cnt1] = v[1][i]; 35 v1[++cnt1] = v[2][i]; 36 v2[++cnt2] = v[3][i]; 37 v2[++cnt2] = v[4][i]; 38 } 39 //v[1] , v[2] 都取 40 for(int i = 1; i <= n; ++i){ 41 for(int j = 1; j <= n; ++j){ 42 v1[++cnt1] = v[1][i] + v[2][j]; 43 if(!v1[cnt1]) flag1 = 1; 44 v2[++cnt2] = v[3][i] + v[4][j]; 45 if(!v2[cnt2]) flag2 = 1; 46 } 47 } 48 sort(v1 + 1,v1 + cnt1 + 1); 49 sort(v2 + 1,v2 + cnt2 + 1); 50 int flag = 0; 51 for(int i = 1; i <= cnt1; ++i){ 52 int pos = lower_bound(v2 + 1,v2 + cnt2 + 1,k - v1[i]) - v2; 53 if(v1[i] + v2[pos] == k){ 54 if(v1[i] == 0 && v2[pos] == 0 && flag1 == 0 && flag2 == 0) 55 continue; 56 flag = 1; 57 break; 58 } 59 } 60 if(flag) printf("YES\n"); 61 else printf("NO\n"); 62 } 63 return 0; 64 }
10.一个找规律的水题,就是对于所有的五位数,只要第一位×第二位=45位,然后第一位+第二位个位数为第三位,则符合题目的规律,从小到大输出所有符合要求的五位数。
至此,第一场新生赛over,好累TUT。