【题解】吉首大学第六届新星杯暨程序设计大赛(新生网络同步赛).2016

版权声明:原创文章转发转载请注明出处,盗版必究 https://blog.csdn.net/Fantastic_/article/details/53866444

 

A

题目描述

C语言函数,数学函数,傻傻分不清楚~~

题目很简单,我们定义F(x)是满足x取余a乘b的积等于0(即:x%(a*b)==0)这样的a,b的组数。现在给你一个n,你需要求出 F(n)。

比如说当n=4时,a b分别可以是——1*1、1*2、1*4、2*1、2*2、4*1,共6种情况,所以F(4) = 6。

输入

多组输入(不会超过200组)
每组测试数据输入一个整数n (1 <= n <= 10^8)

输出

每组测试数据输出 Case x: y ,x 表示第x组测试数据,y表示F(n)的值,细节参考样例。

样例输入

1
2
3
4

样例输出

Case 1: 1
Case 2: 3
Case 3: 3
Case 4: 6
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 10009
using namespace std;
int a[maxn+10];
int prime[maxn+10],p;
void init_p(){
    p=0;
    memset(a,0,sizeof(a));
    for(int i=2;i<maxn;i++){
        if(a[i]==0)prime[p++]=i;
        for(int j=1;j<p&&prime[j]*i<maxn;j++){
            a[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
int main(){
    init_p();
    int cas=1;
    long long n;
    while(scanf("%lld",&n)!=-1){
        memset(a,0,sizeof(a));
        for(int i=0;i<p;i++){
            while(n%prime[i]==0){
                a[i]++;
                n/=prime[i];
            }
        }
        long long ans = ((n==1)?n:3),t;
        for(int i=0;i<p;i++){
            t=(long long)(a[i]+2)*(a[i]+1)/2;
            ans*=t;
        }
        printf("Case %d: %lld\n",cas++,ans);
    }
    return 0;
}
/**
一个分解因数题,根据唯一分解定理,任何一个数可以分解成 p1^e1*p2^e2*p3^e3...(pi为素数,ei为指数),其中ei序列唯一;
根据题意,求的就是n的约数个数
假设,n=p^x;在n的约数中,取p的有(x+1)种情况
设n%(a*b)中a = p^i,
则此时b可取p^0,p^1...p^(x-i), 共 (x-i+1) 个(a,b);
另i:0->x, 则共有sum(x)种合法答案!

如8:(下列冒号前表示a,冒号后表示b)
1:1,2,4,8
2:1,2,4
4:1,2
8:1

得出:f(p^k) = sum(k+1);
对于f(p*q);
如果令f(q)的分解a*b恒为1(即无视),则f(p*q)=f(q);
同理令f(p)的分解为1,f(p*q)=f(q);
根据乘法定理: f(p*q)=f(p)*f(q);


另外还有一种比较明显的思路:
要使得n%(a*b)==0就是令a*b为n的因子,
设a恒为1,则b可取数为n的因子个数;
再对b就行一次分解即可(这节比较重要);
还是可以用8做例子:
1:1		1*(1*1)
2:1,2		1*(2*1),1*(2*2)
4:1,2,2		1*(4*1),1*(2*2),1*(2*2)
8:1,2,4,8	1*(8*1),1*(2*4),1*(4*2),1*(1*8)
同样还是sum!
*/

 

 

B

题目描述

“回文”是指正读反读都能读通的句子,它是古今中外都有的一种修辞方式和文字游戏,如“我为人人,人人为我”等。在数学中也有这样一类数字有这样的特征,成为回文数

   现在出题如下:

    对于10进制数87:

    STEP1:87+78  = 165                  STEP2:165+561 = 726

    STEP3:726+627 = 1353                STEP4:1353+3531 = 4884

  上例用了4步得到回文数,在这里的一步是指进行了一次N进制的加法

    写一个程序,给定一个N(2<=N<=10)进制数 , M(小于1000位数),求最少经过几步可以得到回文数。

    如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”

 

输入

N和M

 

输出

步数

 

样例输入

9
87

样例输出

STEP=6
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 30009
char a[maxn],b[maxn];
int n;
void rev(char*s){//反转s
    int j = strlen(s)-1,i=0;
    char t;
    while(i<j){
        t= s[i];
        s[i++]=s[j];
        s[j--]=t;
    }
}
int check(char *a){//检查回文
    strcpy(b,a);
    rev(b);
    return strcmp(a,b);
}
int add(){//n进制加法
    strcpy(b,a);
    rev(b);
    int len = strlen(a),bit=0,t,i;
    for(i=0;i<len;i++){
        t = (a[i]-'0')+(b[i]-'0')+bit;
        a[i]=t%n+'0';
        bit = t/n;
    }
    if(bit)
        a[i]=bit+'0';
    return check(a);
}
int main(){
    scanf("%d",&n);
    scanf("%s",a);
    rev(a);
    int ans = 1;
    if(check(a)==0){
        ans = 0;
        printf("STEP=0\n");
    }
    for(int i=1;ans&&i<=30;i++){
        if(add()==0){
            printf("STEP=%d\n",i);
            ans = 0;
            break;
        }
    }
    if(ans)printf("Impossible!\n");
    return 0;
}
/**
这是一道按照说明模拟就可以出答案的题;
*/

 

C

题目描述

  话说,小C经过上次被小T实力坑了一把以后呀,他就决定发明一个数字游戏来坑一坑小T!游戏规则是这样~

在游戏开始前,小C会任意的选出一个正整数n(1≤n≤2^32-1),同时令m=1。在玩的过程当中,小C和小T每个人都可以将m的值扩大2到9中的任意倍数(倍数一定是整数),两个人一人扩大一次,第一个使m≥n的人就是最后的赢家。

因为两个人都在互相算计,所以都是用的最佳策略,问最后是谁赢?

(上次因为吃了先手的亏,小C决定先手,也就是说:每次都是小C第一个玩)。

输入

多组输入(文件尾结束)

每行一个正整数n

输出

对于每个结果:

如果小C赢,则输出"C",

如果小T赢,则输出"T"。

(记得换行!!)

样例输入

9

样例输出

C
#include <cstdio>
#include <cmath>
#define maxn 4294967295L
long long arr[100];
int p;
void init(){
    int i;
    for(i=1;;i++){
        arr[i]=(long long)pow(18,i);
        if(arr[i]>=4294967295L)break;
    }
    p=i;
}
int main(){
    init();
    long long n;
    while(~scanf("%lld",&n)){
        int i;
        for(i=1;i<p;i++)
            if(arr[i]>=n)break;

        if(n>arr[i]/2)printf("T\n");
        else printf("C\n");
    }
    return 0;
}
//4294967295
/**
博弈论!
对于这中不公平博弈,先手是很占优势的;这类题的通解思路:
如果先手可以到达终止局面,直接跳终止局面获得胜利
否则,把对手推到必败局面,相当于获得胜利!
再否则,开局投降无需挣扎....
思路很简单,关键在找必败局面:
把问题稍微转换一下,便于找必败局面: 把m每次乘以2-9直到大于等于n -> 把n每次除以2-9(向上取整)直到n<=1为获胜;
那么可以一次到达目标局的区间是[1-9]
第一个必败局面是(9,18],因为除9达不到1,除其他数对手必胜;
同理可以一次到达(9-18]的必败局(新目标局)的区间是(19,18*9]
再推一个必败局(18*9,18*9*2],再推一个必胜局[18*9*2+1,18*9*2*9]
再推一个必败局(18*9*2*9,18*9*2*9*2]
呵呵,找到规律了吗?
*/

 

D

题目描述

有小明和小曹两个无聊的人,对字符串操作起了兴趣,小曹给出一个由小写字母构成的字符串,小明给出另一个比小曹更长的字符串,也由小写字母组成,如果能通过魔法转换使小明的串和小曹的变成同一个,那么他们两个人都会很开心。这里魔法指的是小明的串可以任意删掉某个字符,或者把某些字符对照字符变化表变化。如: 
小曹的串是 abba; 
小明的串是 addba; 
字符变化表 d b (表示d能转换成b)。 
那么小明可以通过删掉第一个d,然后将第二个d转换成b将串变成abba。 
现在请你帮忙判断:他们能不能通过魔法转换使两个人的串变成一样呢?

 

输入

 首先输入T,表示总共有T组测试数据(T <= 40)。 
  接下来共T组数据,每组数据第一行输入小曹的字符串,第二行输入小明的字符串(数据保证字符串长度不超过1000,小明的串的长度大于等于小曹的,且所有字符均为小写字母)。接着输入字母表,先输入m,表示有m个字符变换方式(m< = 100),接着m行每行输入两个小写字母,表示前一个可以变为后一个(但并不代表后一个能变成前一个)。

 

输出

 对于每组数据,先输出Case数。 
如果可以通过魔法转换使两个人的串变成一样,输出“happy”, 
否则输出“unhappy”。 
每组数据占一行,具体输出格式参见样例。

 

样例输入

2
abba
addba 
1
d b
a
dd
0

样例输出

Case #1: happy
Case #2: unhappy
#include <cstdio>
#include <cstring>
#define maxn 30
char a[1009],b[1009];
int g[30][30];
void init(){
    for(int i=0;i<maxn;i++)g[i][i]=1;

    for(int k=0;k<maxn;k++)
    for(int i=0;i<maxn;i++)
    for(int j=0;j<maxn;j++)
            if(g[i][k]&&g[k][j])
                    g[i][j]=1;
}
int check(){
    int i,j,la = strlen(a),lb=strlen(b);
    int ai,bi;
    for(i=0,j=0;i<la&&j<lb;j++){
        ai = a[i]-'a';
        bi = b[j]-'a';
        if(g[bi][ai])
            ++i;

    }
    return i==la;
}
int main(){
    int T,m;
    char s[5],t[5];
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
        scanf("%s%s%d",a,b,&m);
        memset(g,0,sizeof(g));
        for(int i=0,si,ti;i<m;i++){
            scanf("%s%s",s,t);
            si = s[0]-'a';
            ti = t[0]-'a';
            g[si][ti]=1;
        }
        init();
        printf("Case #%d: ",cas);
        if(check())printf("happy");
        else printf("unhappy");
        printf("\n");
    }
    return 0;
}
/**
题目意思开始不太明白,输入串a,b以及变换规则,如果b可以删除一些字符,在由变换规则转换一些字符可以变成a,则happy;
思路也很质朴:
把变换规则保存在图g中,通过floyd调整节点连通性(调整搜索也可)
遍历b,a;贪心匹配:
如果bi==ai,都跳下一个
否则如果bi到ai在图中连通,都跳下一个;
否则b跳下一个(相当于删一个字符)
最后,如果a遍历完了,就说明OK了;

*/

 

E

题目描述

上了大学的小明又在做题了,他好累,所以请你帮忙:

设n为正整数,令f(n)为所有gcd(x,y)的最大值,且x和y满足1<=x<y<=n,其中gcd指最大公约数。

举个例子:当n=3时,x,y可以取1,2或1,3或2,3,gcd(x,y)的最大值为1,因此f(3)=1。

给定正整数n,求当2<=i<=2*n+1时,所有f(i)的平方和。

 

 

输入

多组输入,最多100组

每组一个正整数 n < 10^9.

 

输出

仅一行,所求结果对10007取模的结果。

 

样例输入

3

样例输出

28
#include <cstdio>
#define MOD 10007
int main(){
    int n;
    long long ans;
    while(~scanf("%d",&n)){
        if(n%3==0){
            ans = n/3;
            ans = (ans%MOD*(n+1)%MOD*(2*n+1)%MOD)%MOD;
        }else if((n+1)%3==0){
            ans = (n+1)/3;
            ans = (ans%MOD*(n)%MOD*(2*n+1)%MOD)%MOD;
        }else{
            ans = (2*n+1)/3;
            ans = (ans%MOD*(n)%MOD*(n+1)%MOD)%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
/**
有意思的是这个样例,如果样例ok就基本没问题了{ps:恰好卡了:( }
3
i数是:2,3,4,5,6,7
f(i) :1,1,2,2,3,3	(5为什么是2,7为什么是3呢?)
没错,就是这个规律: ans= (1^2+2^2...n^2)*2;
哈哈公式不记得吧?懵逼了?
1^2+2^2+..+n^2 = n(n+1)(2n+1)/6;
证明一下: 
给定一个由数字组成的正三角形,第一行1个1,第二行2个2,第三行3个3,第N行n个n(这个三角所有和是什么?),按顺时针转60度,2次,每次得一个新三角形;
把这三个三角形对应位置数相加到结果三角形中,则结果三角中每个数都变成了 2n+1
结果三角中一共有多少数: 1+2+...n= n(n+1)/2
sum*3 = (2n+1)*n(n+1)/2
证毕!

最后须知 , 在 n, n+1 , 2n+1 这三个数中,始终有一个是3的倍数:
不妨设n非3的倍数,那么n%3可为1,2
为2: 则 n+1为3的倍数
为1: 则2n+1为3的倍数( (2n+1)%3== 2%3*n%3+1%3=6  )

*/

 

 

F

题目描述

拉丁方的定义 : n个不同的符号(这里取1到n),在n*n的方阵中每个符号在每一行和每一列均只出现一次,那么我们称此方阵为n阶的拉丁方

最小n阶拉丁方最小拉丁方中每行所表示的十进制数最小

 

输入

多组输入,每组一个n ( n <= 12 ) 

 

输出

输出最小拉丁方,每组输出数据之间用一个换行隔开

 

样例输入

1
2

样例输出

1

1 2
2 1

 

#include <cstdio>
#include <cstring>
int g[15][15],c[15][15],r[15][15],n,ok;
void cdfs(int i,int j){//按列搜
    if(j==n||ok){
        ok=1;
        return ;
    }
    int x;
    for(x=1;x<=n;x++)
        if(!c[j][x]&&!r[i][x]&&!g[i][j]){
            c[j][x]=r[i][x]=1;
            g[i][j]=x;
            cdfs(i,j+1);
            if(ok)return ;
            g[i][j]=0;
            c[j][x]=r[i][x]=0;
        }
    if(i==n-j-1)cdfs(i,j+1);
}
void rdfs(int i){//按行搜
    if(i==n){
        for(int k=0;k<n;k++){
            for(int j=0;j<n;j++)
                printf((j==n-1)?"%d\n":"%d ",g[k][j]);
        }
        printf("\n");
        return;
    }
    ok=0;
    cdfs(i,1);
    rdfs(i+1);
}
int main(){
    while(~scanf("%d",&n)){
        memset(g,0,sizeof(g));
        memset(c,0,sizeof(c));
        memset(r,0,sizeof(r));
        for(int i=0;i<n;i++){
            g[0][i]=g[i][0]=i+1;
            g[i][n-i-1]=n;
            r[i][n]=c[i][n]=1;
            r[i][i+1]=c[i][i+1]=1;
        }
        rdfs(1);

    }
    return 0;
}
/**
没法理解题目,根据错误数据判断出来这么做是对的;(为什么不比赛的时候多提交几次套套答案呢?)
5
1 2 3 4 5
2 1 4 5 3
3 4 5 1 2
4 5 2 3 1
5 3 1 2 4

6
1 2 3 4 5 6
2 1 4 3 6 5
3 4 5 6 1 2
4 3 6 5 2 1
5 6 1 2 3 4
6 5 2 1 4 3
规律就是: 副对角线为n,1行与1列递增,其他位置从小到大,可行即填入;
写了一波垃圾剪枝
*/

 

 

 

 

 

G

题目描述

问题很简单,找出现了3*k-1次的数 ( k > 0 )

 

输入

输入一个 n 接下来就是n个数

确保有一个数出现了 3*k-1次,其余数都出现了3*m 次 (m > 0 )

对于所有的数在int范围内,且n小于216660

 

输出

找出现了3*k-1次 的那个数

 

样例输入

5
3 3 3 2 2 

样例输出

2
#include <cstdio>
using namespace std;
#define maxn 32
int bit[maxn];
int main(){
    int n,x,ans=0;
    scanf("%d",&n);
    while(n--){
        scanf("%d",&x);
        for(int i=0;x;i++){
            bit[i]+=x%2;
            x/=2;
            bit[i]%=3;
        }
    }
    for(int i=0;i<maxn;i++){
        ans+=bit[i]*(1<<i);
    }
    printf("%d\n",ans/2);
    return 0;
}
/**
异或和二进制永远都是神器
把所有输入数的二进制保存起来,
出现3k次的,其二进制的某位一定为3的倍数;(3k-1就是2的倍数嘛)
*/

 

H

题目描述

     小C和小T骑车去郊游,小T先出发,速度为x米每分钟,行驶m分钟后,小C带着一条狗出发,小C的速度为y米每分钟,而狗则以每分钟z米的速度向小T跑去,追上小T后又会返回,如此往复,直到小C追上小T。问:当小C追上小T的时候,狗跑了多远?

输入

第一行输入一个整数N,表示测试数据的组数(N<100)
每组测试数据占一行,是四个正整数,分别为m,x,y,z(数据保证X<Y<Z)

输出

输出狗跑的路径长度,结果保留小数点后两位。

样例输入

1
5 10 15 20

样例输出

200.00
#include <cstdio>
int a[4];
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        for(int i=0;i<4;i++)
            scanf("%d",a+i);
        double ans = ((1.0*a[0]*a[1])/(a[2]-a[1])*a[3]);
        printf("%.2lf\n",ans);
    }
    return 0;
}
/**
看完的第一反应是狗沿直线跑很远很远,超过了所有人!
*/

 

I

题目描述

 

 

小小成现在拥有n根火柴,假设小小成只能按照上图的方式用火柴摆数字,并且不能进行任何运算,而且必须

用完所有火柴棍,请问能摆出来的最大数字次大数字是多少 。

 

 

输入

多组输入( 最多 2000 组 ) 

每组数据仅一行,包含一个正整数 n 。

  1 ≤ n ≤2000

 

输出

对于每组数据输出一行,如果能摆出来的最大数字次大数字都存在,则这一行包含两个非负整数,表示能摆出来的最大数字和次大数字

否则在这一行输出 small small cheng is silly boy

 

样例输入

5
1

样例输出

71 17
small small cheng is silly boy
#include <cstdio>
char ans[10009];
int ap;
void solve(int n){
    int a=0,b=0;
    if(n%2==0)
        a=n/2;
    else {
        ans[ap++]='7';
        a=(n-3)/2;
    }
    while(a--) ans[ap++]='1';
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        ap=0;
        if(n<4)printf("small small cheng is silly boy\n");
        else if(n==4)printf("11 4\n");
        else{
            solve(n);
            ans[ap]=0;
            if(ans[0]=='1'){
                printf("%s ",ans);
                ans[0]=ans[1]='7';
                ans[ap-1]=0;
                printf("%s\n",ans);
            }else{
                printf("%s ",ans);
                ans[0]^=ans[1]^=ans[0]^=ans[1];
                printf("%s\n",ans);
            }
        }
    }
    return 0;
}
/**
观察图:能拼出数字的需要火柴:2,3,4,5,6,7; 分别对应数字1,7,4,5,9,8
根据贪心的原则,肯定要多拼出几位数啊.因为4=2+2,5=2+3,6=3+3,7=2+2+3...
所以只有2,3有效,也就是说,拼出的数字只有1,7
有坑!
考虑5根以下火柴:
1:没用
2:只能拼出1,没有次大数
3:只能拼出7
4:只能拼出11?(呵呵,一个思维死角)

如果最大数7打头,7后移一位就是次大数
如果最大数全为1,把头三个1转换成77就是次大数
*/

 

J

题目描述

    实验室除了是一个学习的地方,更是一个非常好玩的地方,有各种各样的活动充斥在学习之间。

    在某一次活动中,黄sir带着大家一起去爬山。历经了千辛万苦后终于爬上了山顶,大家是又累又渴。。但是在山顶,只有一口井,每次只能容下一个人去打水。那么问题来了!

  现在假设实验室有n个人,每个人打水的时间为ai,请给出一个排队的规则,使所有人的平均等待时间最小

注意:若两个人的等待时间相同,则序号小的优先。

输入

多组输入

每组两行

第一行为n,表示有多少个人

第二行有n个数,分别表示每个人打水的时间a[1],a[2],a[3],a[4],……a[n],每个数据之间有一个空格

数据范围: 0<n<=1000, 0<a[i]<=1000 

输出

对于每组输入

输出有两行(格式见样例)

第一行为使所有人的平均等待时间最小的排队顺序

第二行为最小的平均等待时间(输出结果精确到小数点后两位)

样例输入

10
56 12 1 99 1000 234 33 55 99 812

样例输出

3 2 7 8 1 4 9 6 10 5
291.90

提示

错误已更正~~

#include <cstdio>
#include <algorithm>
#define maxn 1009
struct A{
    int id;
    int val;
    bool operator<(const A&x)const{
        if(val!=x.val)
            return val<x.val;
        else return id<x.id;
    }
}a[maxn];
int main(){
    int n;
    double sum=0;
    while(~scanf("%d",&n)){
        sum=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a[i].val);
            a[i].id=i+1;
        }
        std::sort(a,a+n);
        if(a[n-1].val==a[n-2].val)
        {//这是什么?
            A t = a[n-2];
            a[n-2]=a[n-1];
            a[n-1]=t;
        }
        for(int i=0;i<n;i++){
            if(i!=n-1)sum+=a[i].val*(n-i-1);
            printf((i==n-1)?"%d\n":"%d ",a[i].id);
        }
        printf("%.2lf\n",sum/n);
    }
    return 0;
}
/**
莫名其妙就错了,然后莫名其妙过的;
*/

 

K

题目描述

最近发现了一些神奇的数字,就是各个位都不相同的四位数,如1234 , 把所有数字从大到小排序后得到的数,减去从小到大得到的数,一直如此减下去,竟然一定会得到6174,那么你能求出要多少次这样的操作才能得到6174吗?

例如,从1234出发,依次可以得到4321-1234=3087、8730-378=8352、8532-2358=6174。这样就回到了6174。操作步数为3,所以输出3。

输入

第一行输入n,代表有n组测试数据。
接下来n行每行都写一个各位数字互不相同的四位数

输出

经过多少次上面描述的操作才能出现6174。

样例输入

1
1234

样例输出

3
#include <cstdio>
#include <algorithm>
#include <cstring>
char a[10],b[10];
int solve(){
    int cnt = 0,i,j,ai,bi;
    while(strcmp("6174",a)){
        std::sort(a,a+4);
        strcpy(b,a);
        i=0;j=strlen(b)-1;
        while(i<j){
            b[i]^=b[j]^=b[i]^=b[j];
            ++i;
            --j;
        }
        sscanf(a,"%d",&ai);
        sscanf(b,"%d",&bi);
        sprintf(a,"%d",bi-ai);
        ++cnt;
    }
    return cnt;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",a);
        int ans = solve();
        printf("%d\n",ans);
    }
    return 0;
}
/**
算不上搜索的搜索,强行搜索
*/

 

 

 

 

 

 

没有更多推荐了,返回首页