2016中国大学生程序设计竞赛 - 网络选拔赛题解报告

51 篇文章 0 订阅
21 篇文章 0 订阅

此文章可以使用目录功能哟↑(点击上方[+])

好弱呀,感觉要打铁...

链接→2016中国大学生程序设计竞赛 - 网络选拔赛

 Problem 1001 A water problem

Accept: 0    Submit: 0
Time Limit: 5000/2500 MS (Java/Others)    Memory Limit : 65536/65536 K (Java/Others)

 Problem Description

Two planets named Haha and Xixi in the universe and they were created with the universe beginning.

There is 73 days in Xixi a year and 137 days in Haha a year. 

Now you know the days N after Big Bang, you need to answer whether it is the first day in a year about the two planets.

 Input

There are several test cases(about 5 huge test cases).

For each test, we have a line with an only integer N(0≤N), the length of N is up to 10000000.

 Output

For the i-th test case, output Case #i: , then output "YES" or "NO" for the answer.

 Sample Input

10001
0
333

 Sample Output

Case #1: YES
Case #2: YES
Case #3: NO

 Problem Idea

解题思路:

【题意】
两个星球从宇宙大爆炸开始就产生了

Xixi星球一年有73天,Haha星球一年有137天

问宇宙大爆炸开始第N天是否是两个星球一年当中的第一天


【类型】
同余定理

【分析】
认真读题,我们可以发现如果第N天是两个星球一年当中的第一天

那么N必定能被73整除,同时能被137整除

就这样,题目发生了转变,然而此题还没有就此解决

因为N这个数长度可达10000000,显然这是个极大的数

那我们就需要采取同余定理求解

首先,我们知道模运算是不影响乘法和加法的

所以一个数,比如23,对5取模,23≡2*10+3(mod 5)

故,这个巨大的数N可以拆解成一位一位取模,这样,此题就可以得到圆满解决

【时间复杂度&&优化】
O(strlen(n))

题目链接→HDU 5832 A water problem

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 10000005;
const int M = 10005;
const int inf = 1000000007;
const int mod = 1000000007;
char s[N];
int main()
{
    int ans1,ans2,p=1,i,k;
    while(~scanf("%s",s))
    {
        ans1=ans2=0;
        k=strlen(s);
        for(i=0;i<k;i++)
        {
            ans1=(ans1*10+s[i]-'0')%73;
            ans2=(ans2*10+s[i]-'0')%137;
        }
        if(!ans1&&!ans2)
            printf("Case #%d: YES\n",p++);
        else
            printf("Case #%d: NO\n",p++);
    }
    return 0;
}

 Problem 1002 Zhu and 772002

Accept: 0    Submit: 0
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit : 65536/65536 K (Java/Others)

 Problem Description

Zhu and 772002 are both good at math. One day, Zhu wants to test the ability of 772002, so he asks 772002 to solve a math problem. 

But 772002 has a appointment with his girl friend. So 772002 gives this problem to you.

There are n numbers a1,a2,...,an. The value of the prime factors of each number does not exceed 2000, you can choose at least one number and multiply them, then you can get a number b.

How many different ways of choices can make b is a perfect square number. The answer maybe too large, so you should output the answer modulo by 1000000007.

 Input

First line is a positive integer T , represents there are T test cases.

For each test case:

First line includes a number n(1≤n≤300),next line there are n numbers a1,a2,...,an,(1≤ai≤10^18).

 Output

For the i-th test case , first output Case #i: in a single line.

Then output the answer of i-th test case modulo by 1000000007.

 Sample Input

2
3
3 3 4
3
2 2 2

 Sample Output

Case #1:
3
Case #2:
3

 Problem Idea

解题思路:

【题意】
有n个数,每个数的质因数不会超过2000
问从n个数中至少选取1个数,它们的乘积是完全平方数的情况有多少种,结果对1000000007取模


【类型】
高斯消元

【分析】
首先,我们知道的是,完全平方数的各种质因子必定出现偶数次

不然不可能被开方

例如36=2*2*3*3

质因子2,3均出现两次

所以呢,此题已经可以转化为类似开关问题的高斯消元了


这里,,表示第i个数取或不取

,表示质因子出现偶数次

,表示(第j个数分解质因数后,2000以内第i个质数出现多少次,奇数次值为1,偶数次值为2)

剩下的就是套一下高斯消元的模板,求解出自由变元的个数ans,那此题的结果就是,这个快速幂求解一下就可以了

如果还是不明白的话,我们来举例说明

就比如样例3,3,4

方程变元x前的系数k为我们打素数表2,3,5,……,1999中第k个质数出现奇数次还是偶数次

那该样例可得方程组为


第一条方程是质数2的贡献,因为3是不包含质因子2的,故贡献为0,而4虽包含质因子2,但出现了偶数次,故贡献同样为0

第二条方程,两个3都贡献了1,而4不包含质因子3,所以无贡献

显然,方程组只有一条方程,但有3个未知数,所以自由变元有2个,而自由变元的取值为{0,1}

故方程组的解有2^2=4种,除去全为0的一种(因为题目指明至少取一个数),剩3种(1,1,0),(0,0,1),(1,1,1)

【时间复杂度&&优化】
O(n^3)

题目链接→HDU 5833 Zhu and 772002

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 305;
const int M = 2001;
const int inf = 1000000007;
const int mod = 1000000007;
int prime[M],k;
bool v[M];
//有equ个方程,var个变元。增广矩阵行数为equ,列数为var+1,分别为0到var
int equ,var;
int a[N][N]; //增广矩阵
int x[N]; //解集
int free_x[N];//用来存储自由变元(多解枚举自由变元可以使用)
int free_num;//自由变元的个数
void get_prime()
{
    k=0;
    for(int i=2;i<M;i++)
        if(!v[i])
        {
            prime[k++]=i;
            for(int j=i;j<M;j+=i)
                v[j]=true;
        }
}
__int64 Quick_Mod(int a,int b)//快速幂
{
    __int64 res = 1,term = a % mod;
    while(b)
    {
        if(b & 1) res = (res * term) % mod;
        term = (term * term) % mod;
        b >>= 1;
    }
    return res;
}
//返回值为-1表示无解,为0是唯一解,否则返回自由变元个数
int Gauss()
{
    int max_r,col,k;
    free_num = 0;
    for(k = 0, col = 0 ; k < equ && col < var ; k++, col++)
    {
        max_r = k;
        for(int i = k+1;i < equ;i++)
        {
            if(abs(a[i][col]) > abs(a[max_r][col]))
                max_r = i;
        }
        if(a[max_r][col] == 0)
        {
            k--;
            free_x[free_num++] = col;//这个是自由变元
            continue;
        }
        if(max_r != k)
        {
            for(int j = col; j < var+1; j++)
                swap(a[k][j],a[max_r][j]);
        }
        for(int i = k+1;i < equ;i++)
        {
            if(a[i][col] != 0)
            {
                for(int j = col;j < var+1;j++)
                    a[i][j] ^= a[k][j];
            }
        }
    }
    for(int i = k;i < equ;i++)
        if(a[i][col] != 0)
            return -1;//无解
    if(k < var) return var-k;//自由变元个数
    //唯一解,回代
    for(int i = var-1; i >= 0;i--)
    {
        x[i] = a[i][var];
        for(int j = i+1;j < var;j++)
            x[i] ^= (a[i][j] && x[j]);
    }
    return 0;
}
int main()
{
    get_prime();
    int t,i,j,n,p=1,c;
    __int64 ans,s;
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        equ=k;var=n;
        for(i=0;i<n;i++)
        {
            scanf("%I64d",&s);
            for(j=0;j<k;j++)
            {
                c=0;
                while(s%prime[j]==0)
                {
                    s/=prime[j];
                    c++;
                }
                if(c&1)
                    a[j][i]=1;
            }
        }
        int r,c;
        ans=Gauss();
        printf("Case #%d:\n%I64d\n",p++,Quick_Mod(2,ans)-1);
    }
    return 0;
}

 Problem 1004 Danganronpa

Accept: 0    Submit: 0
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit : 65536/65536 K (Java/Others)

 Problem Description

Chisa Yukizome works as a teacher in the school. She prepares many gifts, which consist of n kinds with a[i] quantities of each kind, for her students and wants to hold a class meeting. Because of the busy work, she gives her gifts to the monitor, Chiaki Nanami. Due to the strange design of the school, the students' desks are in a row. Chiaki Nanami wants to arrange gifts like this:

1. Each table will be prepared for a mysterious gift and an ordinary gift.

2. In order to reflect the Chisa Yukizome's generosity, the kinds of the ordinary gift on the adjacent table must be different.

3. There are no limits for the mysterious gift.

4. The gift must be placed continuously.

She wants to know how many students can get gifts in accordance with her idea at most (Suppose the number of students are infinite). As the most important people of her, you are easy to solve it, aren't you?

 Input

The first line of input contains an integer T(T≤10) indicating the number of test cases.

Each case contains one integer n. The next line contains n (1≤n≤10) numbers: a1,a2,...,an, (1≤ai≤100000).

 Output

For each test case, output one line containing “Case #x: y” (without quotes) , where x is the test case number (starting from 1) and y is the answer of Chiaki Nanami's question.

 Sample Input

1
2
3 2

 Sample Output

Case #1: 2

 Problem Idea

解题思路:

【题意】
n种礼物,每种有a[i]个,所有的礼物可以作为普通礼物,也可以作为神秘礼物发放给孩子

现在从第一个人开始,每人可以收到一个普通礼物和一个神秘礼物,相邻两个人不能收到相同的普通礼物,神秘礼物没限制,发完为止

问最多有多少个人可以拿到两件礼物


【类型】
贪心

【分析】
暂时先不考虑普通礼物的限制

那么最多有sum/2个人可以拿到两件礼物(sum为总的礼物件数,即a[1]+a[2]+……+a[n])

那么在考虑普通礼物限制的情况下,我们肯定先要发放普通礼物,直到不能发或超过sum/2为止,这时多出来的礼物就作为神秘礼物补齐发到过普通礼物的孩子

那么说明情况是不能发呢?因为相邻两人要获得不同的普通礼物

所以我们可以借助优先队列,每次将队首的两种礼物取出来发放

假设队首两种礼物的数量为a,b(a>b)

那么可以有2*b个孩子拿到普通礼物,没发完的第一种礼物还剩a-b件,丢进优先队列重新来过

最终得到答案ans,与sum/2取个小值就行了

【时间复杂度&&优化】
O(n)

题目链接→HDU 5835 Danganronpa

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 15;
const int M = 26;
const int inf = 1000000007;
const int mod = 1000000007;
int s[N];
priority_queue<int> q;
int main()
{
    int t,i,n,p=1,sum,k,a,b,ans;
    scanf("%d",&t);
    while(t--)
    {
        ans=sum=k=0;
        while(!q.empty())
            q.pop();
        scanf("%d",&n);
        for(i=0;i<n;i++)
        {
            scanf("%d",&s[i]);
            sum+=s[i];
            q.push(s[i]);
        }
        sum/=2;
        while(!q.empty())
        {
            if(k)
            {
                b=q.top();
                q.pop();
                ans+=2*b;
                q.push(a-b);
            }
            else
            {
                a=q.top();
                q.pop();
            }
            k^=1;
        }
        if(a!=0)
        	ans++;
        printf("Case #%d: %d\n",p++,min(ans,sum));
    }
    return 0;
}

 Problem 1008 Special Tetrahedron

Accept: 0    Submit: 0
Time Limit: 4000/2000 MS (Java/Others)    Memory Limit : 65536/65536 K (Java/Others)

 Problem Description

Given n points which are in three-dimensional space(without repetition).

Please find out how many distinct Special Tetrahedron among them. A tetrahedron is called Special Tetrahedron if it has two following characters.

1. At least four edges have the same length.

2. If it has exactly four edges of the same length, the other two edges are not adjacent.

 Input

Intput contains multiple test cases. 

The first line is an integer T,1≤T≤20, the number of test cases.

Each case begins with an integer n(n≤200), indicating the number of the points.

The next n lines contains three integers xi,yi,zi, (−2000≤xi,yi,zi≤2000), representing the coordinates of the ith point.

 Output

For each test case,output a line which contains"Case #x: y",x represents the xth test(starting from one),y is the number of Special Tetrahedron.

 Sample Input

2
4
0 0 0
0 1 1
1 0 1
1 1 0
9
0 0 0
0 0 2
1 1 1
-1 -1 1
1 -1 1
-1 1 1
1 1 0
1 0 1
0 1 1

 Sample Output

Case #1: 1
Case #2: 6

 Problem Idea

解题思路:

【题意】
在三维空间中给出n个点,问你有多少个特殊四面体

特殊四面体定义如下:
1.至少四条边相等;
2.如果恰好只有四条边相等,那么不相等的两条边不相邻

如图


当AB=AC=BD=CD≠(AD,BC),此时,AD与BC异面(不相邻),满足特殊四面体定义第2条


【类型】
计算几何

【分析】
n^4暴力,但实际上复杂度达不到n^4【虽然我觉得还是可以卡掉的,可能是出题人懒得出这种数据了】
暴力枚举两个点,然后再枚举离这两点距离相同的点,如下图


再枚举四个点,判断其构成的四面体是否四条边相等(即),但是不共面(比如点A,B,C,D,共面可以通过3点(A,B,C)所构成的平面的平面法向量与其中一个向量(A->D)是否垂直来判断)


再判断这个是不是正四面体,如果是正四边形的话,暴力枚举过程会使得该四面体被重复计算6次(四面体4个顶点任取两个,C(4,2)=6)

否则该四面体被重复计算两次(A与D,B与C两种取法)

【时间复杂度&&优化】
O(n^4)

题目链接→HDU 5839 Special Tetrahedron

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 205;
const int M = 2001;
const int inf = 1000000007;
const int mod = 1000000007;
struct node
{
    int x,y,z;
}s[N];
int ans[N];
node xmult(node u,node v)//计算 cross product U x V
{
    node ret;
    ret.x=u.y*v.z-v.y*u.z;
    ret.y=u.z*v.x-u.x*v.z;
    ret.z=u.x*v.y-u.y*v.x;
    return ret;
}
int dmult(node u,node v)//计算 dot product U . V
{
    return u.x*v.x+u.y*v.y+u.z*v.z;
}
node subt(node u,node v)//矢量差 U - V
{
    node ret;
    ret.x=u.x-v.x;
    ret.y=u.y-v.y;
    ret.z=u.z-v.z;
    return ret;
}
node pvec(node s1,node s2,node s3)//取平面法向量
{
    return xmult(subt(s1,s2),subt(s2,s3));
}
bool dots_onplane(node a,node b,node c,node d)//判四点共面
{
    return !dmult(pvec(a,b,c),subt(d,a));
}
int distant(node p1,node p2)
{
    return (p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y)+(p2.z-p1.z)*(p2.z-p1.z);
}
int main()
{
    int t,n,i,j,k,l,p=1,c,ans1,ans2;
    scanf("%d",&t);
    while(t--)
    {
        ans1=ans2=0;
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].z);
        for(i=0;i<n;i++)//第1个点
            for(j=i+1;j<n;j++)//第2个点
            {
                c=0;
                for(k=0;k<n;k++)//第3个点
                {
                    if(k==j||k==i)
                        continue;
                    if(distant(s[k],s[i])==distant(s[k],s[j]))//第3个点到第1个点和第2个点的距离相等
                        ans[c++]=k;
                }
                if(c<=1)//构不成4个点
                    continue;
                for(k=0;k<c;k++)
                    for(l=k+1;l<c;l++)
                    {
                        if(distant(s[ans[k]],s[i])!=distant(s[ans[l]],s[i]))//四条边不相等
                            continue;
                        if(dots_onplane(s[ans[k]],s[i],s[ans[l]],s[j]))//四点共面
                            continue;
                        if(distant(s[ans[k]],s[ans[l]])==distant(s[i],s[j])&&distant(s[i],s[j])==distant(s[ans[k]],s[i]))//六条边相等
                            ans2++;
                        else
                            ans1++;
                    }
            }
        printf("Case #%d: %d\n",p++,ans1/2+ans2/6);
    }
    return 0;
}

 Problem 1011 Lweb and String

Accept: 0    Submit: 0
Time Limit: 2000/1000 MS (Java/Others)    Memory Limit : 65536/65536 K (Java/Others)

 Problem Description

Lweb has a string S.

Oneday, he decided to transform this string to a new sequence. 

You need help him determine this transformation to get a sequence which has the longest LIS(Strictly Increasing). 

You need transform every letter in this string to a new number.

A is the set of letters of S, B is the set of natural numbers. 

Every injection f:A→B can be treat as an legal transformation. 

For example, a String “aabc”, A={a,b,c}, and you can transform it to “1 1 2 3”, and the LIS of the new sequence is 3. 

Now help Lweb, find the longest LIS which you can obtain from S.

LIS: Longest Increasing Subsequence. (https://en.wikipedia.org/wiki/Longest_increasing_subsequence)

 Input

The first line of the input contains the only integer T,(1≤T≤20).

Then T lines follow, the i-th line contains a string S only containing the lowercase letters, the length of S will not exceed 10^5.

 Output

For each test case, output a single line "Case #x: y", where x is the case number, starting from 1. And y is the answer.

 Sample Input

2
aabcc
acdeaa

 Sample Output

Case #1: 3
Case #2: 4

 Problem Idea

解题思路:

【题意】
一个只包含小写字母的字符串S,给每种字母填上相同的数,问得到的数列的最长递增(严格递增)子序列的长度为多少


【类型】
水题

【分析】
因为题目已经规定严格递增

这就使得题目的难度降到了最低

因为相同字母值相同,所以一种字母无论多少个,对LIS的长度贡献只能为1

所以此题就成了求有多少种不同的字母,好吧,暴力扫一遍字符串就可以解决了

【时间复杂度&&优化】
O(n)

题目链接→HDU 5842 Lweb and String

 Source Code

/*Sherlock and Watson and Adler*/
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<complex>
#include<string>
#include<algorithm>
#include<iostream>
#define eps 1e-9
#define LL long long
#define bitnum(a) __builtin_popcount(a)
using namespace std;
const int N = 100005;
const int M = 26;
const int inf = 1000000007;
const int mod = 1000000007;
char s[N];
bool ans[M];
int main()
{
    int t,i,k,p=1;
    scanf("%d",&t);
    while(t--)
    {
        k=0;
        memset(ans,false,sizeof(ans));
        scanf("%s",s);
        for(i=0;s[i]!='\0';i++)
            ans[s[i]-'a']=true;
        for(i=0;i<M;i++)
            if(ans[i])
                k++;
        printf("Case #%d: %d\n",p++,k);
    }
    return 0;
}

菜鸟成长记

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值