2016 寒假训练赛(一) 自我总结

时间:2017-1-18 13:00 - 17:00

全场共七题,四个小时出了四题,反思总结如下:

1.太过追求名次,一道题自己没思路看到很多人都过掉就会感觉慌张,下一场训练赛一定要仔细读题,不要放过每一个细节,另外没有吃透每一道题前不要看rank榜。

2.以后第一次没做出的题有了思路后都要写博客自己再写一遍,加深印象。


下面是题目+思路+代码:

Problem A

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 44   Accepted Submission(s) : 42
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

对于一个01字符串,做如下定义: 如果该串包含奇数个1,则说明它是不平等串,如果包含偶数个1,则说明它是平等串。现在已知一个01字符串S,它的最后一位被替换掉了,如果S是平等串,则末位将被'f'替换,否则将被'u'替换,现在给你一个被替换过的串,你要做的是找出原串并输出。

Input

输入包含多组数据,以"#"结束
每组数据包含一个被替换过的01字符串,串的长度不超过1000.

Output

对于每组数据,输出原串。

Sample Input

101f
010010u
1f
000f
110100101u
#

Sample Output

1010
0100101
11
0000
1101001010

签到题,水~

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1001;
char str[maxn];

int main(){
	while(scanf("%s",str) != EOF){
		if(str[0] == '#')
			break;
		int i,num_1 = 0;
		int len = strlen(str);
		for(i=0 ;i<len-1 ;i++){
			if(str[i] == '1')
				num_1++;
		}
		if(str[len-1] == 'f' && num_1&1){
			str[len-1] = '1';
		}
		else if(str[len-1] == 'u' && !(num_1&1)){
			str[len-1] = '1';
		}
		else{
			str[len-1] = '0';
		}
		puts(str);
	}
	return 0;
}


Problem B

Time Limit : 3000/5000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 129   Accepted Submission(s) : 28
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

众所周知Chaos是个大学霸,他每次考试都是满分,对此Chaos感到很无聊。这天他在纸上写了n个数字,然后想:我能不能考出这些分数呢?假设Chaos初始时可以考到的分数是m分,他第i次考试最多能进步max(k - i, 0)分(i从1开始)。因为Chaos是个学霸,他总能控制他考到的分数x,只要x不超过他所能考到的分数上限。
即假设他第i次考得的分数是Xi, 则有0≤Xi≤Xi-1+max (k - i,0 )。Chaos可以考无数次试。
现在给你Chaos写下的n个分数,请你判断这些分数Chaos是否全部有可能考到(不要求按顺序)
都说了Chaos每次考试都是满分,那么他是怎么能够再进步呢?Chaos对此表示,作为一个学霸,他是无所不能的。

Input

输入包含多组数据
对于每组数据,第一行包括三个整数n,m和k(0≤n,m,k≤106)
第二行包括n个整数ai(0≤ai≤1014),代表Chaos想要考到的分数.

Output

对于每组数据,如果Chaos能考到全部他想要考出的分数,输出"Yes",否则输出"Impossible",不包括引号

Sample Input

5 100 10
63 59 140 30 3
5 0 1
1 2 3 4 1000000000000

Sample Output

Yes
Impossible

开始卡了很久,一直不懂题意,忽略了一个重要条件:0≤Xi≤Xi-1+max (k - i,0 ),也就是说每一次的分数可以是0~最大分数中的任意值,所以只需要判断每一次的当前分加上能加分的最大分(等差数列求和)与目标最大分数的关系。

而小于初始分的目标分可以直接忽略不参与排序。

代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 1;
long long grade[maxn];
long long n,m,k;

int main(){
	int num,i;
	long long t;
	while(scanf("%lld%lld%lld",&n,&m,&k) != EOF){
		num = 0;
		for(i=0 ;i<n ;i++){
			scanf("%lld",&t);
			if(t>m)
				grade[num++] = t;
		}
		sort(grade,grade+num);
		if(k)
			k--;
		for(i=num-1 ;i>=0 ;i--){
			if(grade[i] > m + (k*(k+1)/2)){
				break;
			}
			else{
				m += k;
				k--;
			}
		}
		if(i<0){
			printf("Yes\n");
		}
		else{
			printf("Impossible\n");
		}
	}
	return 0;
}

Problem C

Time Limit : 3000/2000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 66   Accepted Submission(s) : 30
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

 
OSU是一种音乐节奏类打击游戏,游戏者需跟随音乐节奏控制键鼠点击屏幕上出现的各种图形,根据图形的不同,操作方式也不一样,为了使问题更简单,在本题目中,只会出现以下3种不同的图形,并且把所有的圆都视作点
1.Circle Click 在屏幕上(x, y)出现一个圆,操作者需要移动鼠标到(x, y)处并点击
 
2.Segment Move 在屏幕(x1, y1)处出现线段的起点,(x2, y2)处出现线段的终点,操作者需要移动鼠标到坐标(x1, y1)处,保持click,鼠标跟随线段移动至(x2, y2)处
 
3.Back Move 在屏幕(x1, y1)处出现线段的起点,(x2, y2)处出现线段的终点,操作者需要移动鼠标到坐标(x1, y1)处,保持click,鼠标跟随线段移动至(x2, y2)处再返回(x1, y1)
 
现已知鼠标的初始位置在(0, 0)处,按顺序给出N个图形的类别和坐标,求操作者需要移动鼠标的距离之和至少是多少

Input

输入第一行是一个正整数T (T≤100 ),表示有T组数据
对于每组数据,输入第一行是一个正整数N (1≤N≤100 ),表示有N个图形出现
接下来N行,第i行表示第i个出现的图形信信,表达方式有3种:
1 x y 表示该图形是一个Circle Click,坐标是(x, y)
2 x1 y1 x2 y2 表示该图形是一个Segment Move,线段起点是(x1, y1), 终点是(x2, y2)
3 x1 y1 x2 y2表示该图形是一个Back Move,线段起点是(x1, y1), 终点是(x2, y2)
所有坐标的范围都是[-100, 100]

Output

对于每组数据,先输出“Case k: ”,k表示第k组数据,然后输出操作者需要移动鼠标的距离之和的最小值,精确到小数点后6位.

Sample Input

3
1
1 1 1
2
1 1 0
2 1 1 1 2
2
1 1 0
3 1 1 1 2

Sample Output

Case 1: 1.414214
Case 2: 3.000000
Case 3: 4.000000

题目很长,开始看到最优解就先入为主的以为题目很难.....感觉比求最近点对还要难,情况很多,但其实只是一个很水的模拟题,很坑,题目最后有说是按顺序给图形。

代码:

#include <bits/stdc++.h>
using namespace std;

int main(){
	int T,_=1;
	scanf("%d",&T);
	while(T--){
		int n,i,type,xx = 0,yy = 0;
		int x1,y1,x2,y2;
		double ans = 0;
		scanf("%d",&n);
		for(i=0 ;i<n ;i++){
			scanf("%d",&type);
			if(type == 1){
				scanf("%d%d",&x1,&y1);
				ans += sqrt((x1-xx)*(x1-xx) + (y1-yy)*(y1-yy));
				xx = x1,yy = y1;
			}
			else if(type == 2){
				scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
				ans += sqrt((x1-xx)*(x1-xx) + (y1-yy)*(y1-yy));
				ans += sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
				xx = x2,yy = y2;
			}
			else{
				scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
				ans += sqrt((x1-xx)*(x1-xx) + (y1-yy)*(y1-yy));
				ans += 2*sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
				xx = x1,yy = y1;
			}
		}
		printf("Case %d: %f\n",_++,ans); 
	}
	return 0;
}

Problem D

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 57   Accepted Submission(s) : 37
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

一个k位超级素数(Super prime)是指一个k位正整数,它的前1位,前2位, …, 前k位均为素数。例如,239是个3位超级素数,因为2,23,239均为素数。现在的问题是要让输出所有的k位超级素数。
PS:素数是指因子只有1和它本身的数

Input

输入第一行是一个正整数T( T≤100),表示有T组数据。
接下来T行每行包括一个k(1≤k≤8 )。

Output

对于每组数据,输出所有k位超级素数,每个超级素数占一行,按升序输出。

Sample Input

1
1

Sample Output

2
3
5
7

思路:深搜,每一个超级素数都是由前一个比其少一位的超级素数*10加上新的位数得到,因为数据量小,所以可以直接深搜输出,设置上下界避免输出超界。

代码:

#include <bits/stdc++.h>
using namespace std;

int u,v;

bool isprime(int x){
	if(x == 0 || x == 1)
		return false;
	int i;
	for(i=2 ;i<=(int)sqrt(x) ;i++){
		if(x %i == 0)
			return false;
	}
	return true;
}

void dfs(int x){
	int i,num;
	for(i=0 ;i<=9 ;i++){
		num = x + i;
		if(isprime(num)){
			if(num > v)
				return;
			if(num>=u)
				printf("%d\n",num);
			dfs(num*10);
		}
	}
}

int main(){
	int T,n,i;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		u = 1;
		for(i=1 ;i<n ;i++){
			u *= 10;
		}
		v = u*10 - 1;
		dfs(0);
	}
	return 0;
}

Problem E

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 96   Accepted Submission(s) : 40
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

给出n,计算

Input

输入第一行是一个正整数T,表示有T组数据
每组数据包含一个正整数n (n<231 )

Output

对于每组数据,输出一个整数表示答案

Sample Input

2
1
2

Sample Output

1
5

Statistic |  Submit |  Back

水题,循环节为20,所以每个数对20取模,然后套公式sum = (n)*(n+1)(2*n+1)/6即可。

另外好像还有很多其他解法,但我认为打表后找循环节算是很方便的解法了。

代码:

#include <bits/stdc++.h>
using namespace std;

int main(){
	int T,n,ans;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		n %= 20;
		ans = n*(n+1)*(2*n+1)/6;
		ans %= 10;
		printf("%d\n",ans);
	}
	return 0;
}

Problem F

Time Limit : 3000/2000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 57   Accepted Submission(s) : 28
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

笨蛋君得到了一堆糖果,他想将糖果分给他的两个好朋友BD666和BD888。他在地上放n个糖果,每个糖果都有它的价值,分别是v1,v2,v3,v4...vn。笨蛋君先让BD666在地上捡起一部分糖果(不能全捡),随后BD888捡起剩下的全部糖果。然后笨蛋君会检测两人手中糖果的总价值是不是相等。要注意的是笨蛋君是用异或操作来看一个人获得的总价值的。比如BD666捡了3块价值为v1,v2,v3的糖果,笨蛋君就认为他获得的价值是v1 xor v2 xor v3。如果BD666和BD888获得的总价值不相等的话,笨蛋君就会认为BD666破坏了分配的公平性。
然后问题就来了,BD666想问你在不破坏分配公平的情况下,自己能获得的最大的糖果数量是多少。
PS: 异或运算即C++中的“^ ”运算符,特点是按位运算,相同为0,不同为1,例如(1100)2^ (1010)2==(0110)2

Input

输入第一行是一个正整数T,表示有T组数据(1≤T≤100 )。
对于每组数据,第一行是一个正整数n(2≤n≤100000 )
第二行包含n个正整v1, v2, v3, v4...vn. (vi<231 )

Output

输出BD666所能获得的最大糖果数量,如果BD666无论如何都不能让笨蛋君满意的话(即破坏了分配的公平性),输出-1。

Sample Input

2
2
1 2
2
1 1

Sample Output

-1
1

如果异或和相等,则两个和再异或一定为零,所以可以把所有的数都异或起来,如果值不为零,则输出-1,否则输出n-1,因为若x ^ y == 0,那么x == y,则n个数,任意n-1个数全部异或的和一定与剩下的那个数相等。

代码:


#include <bits/stdc++.h>
using namespace std;
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
        int n,i,sum = 0,t;
        scanf("%d",&n);
        for(i=0 ;i<n ;i++){
        scanf("%d",&t);
        sum ^= t;
        }
        if(sum == 0)
        printf("%d\n",n-1);
        else
        printf("-1\n");
	}
	return 0;
}


Problem G

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/102400K (Java/Other)
Total Submission(s) : 62   Accepted Submission(s) : 18
Font: Times New Roman | Verdana | Georgia
Font Size:  

Problem Description

You are given an integer array s[] and are asked to counthow many positions a, b, c and d satisfy the condition: s[a] + s[b] + s[c] == s[d].
Note that a, b, c, and d do not need to be distinct.

Input

The first line of input contains an integer T, indicates the cases.
Each of the next Tblocks contains an integer n first (0< n ≤1000 ), the length of the array s[], following with n integers representing s[] (0≤ si≤100000 ).

Output

Output Tlines each contains the answer required. You'd better use 'long long' instead of 'int'.

Sample Input

4
4
0 0 0 0
2
1 3
4
1 10 100 111
1
3

Sample Output

256
1
6
0
这道题才是最气的,之前做过一道完全相当于一模一样的题,之前的思路就错了一直WA,然后今天又走的错误的老思路,超时到底。回归正题,这题可以移项,然后两两一对来降低复杂度,然后一组用HASH来存数目,一组直接映射查找,累加数目即可。

代码:


#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int v[maxn];
int Hash[200005];

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int i,j,n;
		scanf("%d",&n);
		for(i=0 ;i<n ;i++){
			scanf("%d",&v[i]);
		}
		memset(Hash,0,sizeof(Hash));
		for(i=0 ;i<n ;i++){
			for(j=0 ;j<n ;j++){
				Hash[v[i]+v[j]]++;
			}
		}
		long long ans = 0;
		for(i=0 ;i<n ;i++){
			for(j=0 ;j<n ;j++){
				if(v[i]-v[j]>=0){
					ans += Hash[v[i]-v[j]]; 
				}
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值