A题:
题目描述:
给定一个数列,数列的规律是1,1,2,1,2,3,1,2,3,4....的形式依次递增,问这个数列中第n个数是多少?(1<=n<=10^14)
解析:
一开始我看到n的量级达到了10^14,所以我就没往暴力遍历的方向上去想,于是就开始寻找一般规律,然后发现这个其实就是解一个一元二次方程的正整数解(根据等差数列前n项和的公式),相当于前n-1个整数块都是完整的,然后n减去前n-1个完整的整数块,得到不完整的整数块遍历到的数字。如果这个一元二次方程的解是相同的,那么说明刚好到达第n个整数块的末尾,然后输出这个解即可。
完整代码实现:
#include<cstdio>
#include<algorithm>
#include<cmath>
typedef long long ll;
int main()
{
ll n;
while(scanf("%I64d",&n)==1)
{
ll flag = (sqrt(8*n+1) - 1) / 2;
//printf("flag = %I64d\n",flag);
if(ll(sqrt(8*n+1)) * ll(sqrt(8*n+1)) == 8*n+1)
{
printf("%I64d\n",flag);
}
else
{
ll all_pre = (flag + 1) * flag / 2;
printf("%I64d\n",n-all_pre);
}
}
return 0;
}
需要注意的一个问题是:在if判断中左边的表达式sqrt()函数的返回值是浮点型的,而右边表达式的值是长整型,不同编译器处理可能会出现不同的问题,因此需要对左边sqrt()的返回值进行强制转换。
然后我发现别人都是用直接暴力的方法一个个遍历过去,于是我自己也尝试了,发现并没有超时,下面是测试:
所以在以后有想法的时候,可以用大数据测试一下,并用计算机计算一下运行时间,若未超时,即可提交。
完整代码实现:
#include<cstdio>
//#include<ctime>
#include<iostream>
//using namespace std;
typedef long long ll;
int main()
{
ll n;
//int start,finish;
while(scanf("%I64d",&n)==1)
{
int i = 0;
//start = clock();
while(n > i)
{
n -= i;
i++;
}
//finish = clock();
//cout << finish - start << "ms" <<endl; //(以毫秒为计时单位)
printf("%I64d\n",n);
}
return 0;
}
B题:B题题目链接
题目描述:
给定一个时间,然后按照给定格式输出a分钟后的时间
解析:
这道题是一道水题,只要按照题目要求,并且分类讨论即可
完整代码实现:
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int old_hour,old_minute,a,new_hour,new_minute;
while(scanf("%d:%d%d",&old_hour,&old_minute,&a)==3)
{
int hour,minute;
hour = a / 60;
minute = a % 60;
new_minute = (old_minute + minute) % 60;
new_hour = (old_hour + hour + (old_minute + minute) / 60) % 24;
if(new_hour < 10 && new_minute >= 10)
{
printf("0%d:%d\n",new_hour,new_minute);
}
else if(new_hour >= 10 && new_minute < 10)
{
printf("%d:0%d\n",new_hour,new_minute);
}
else if(new_hour >= 10 && new_minute >= 10)
{
printf("%d:%d\n",new_hour,new_minute);
}
else if(new_hour < 10 && new_minute < 10)
{
printf("0%d:0%d\n",new_hour,new_minute);
}
}
return 0;
}
C题:
C题题目链接
题目描述:
You are given array a with n integers and m queries. The i-th query is given with three integers li, ri, xi.
For the i-th query find any position pi (li ≤ pi ≤ ri) so that api ≠ xi.
The first line contains two integers n, m (1 ≤ n, m ≤ 2·105) — the number of elements in a and the number of queries.
The second line contains n integers ai (1 ≤ ai ≤ 106) — the elements of the array a.
Each of the next m lines contains three integers li, ri, xi (1 ≤ li ≤ ri ≤ n, 1 ≤ xi ≤ 106) — the parameters of the i-th query.
Print m lines. On the i-th line print integer pi — the position of any number not equal to xi in segment [li, ri] or the value - 1 if there is no such number.
6 4 1 2 1 1 3 5 1 4 1 2 6 2 3 4 1 3 4 2
2 6 -1 4
给定一个两个整数n,m,分别表示数组中元素的个数以及询问的次数,然后接下来的一行是数组a中的n个元素,编号从1开始,接着接下来的四行,每行三个整数,前两个整数是区间范围,左闭右闭,第三个整数是查询的数字x,问在给定区间内,输出任意一个不等于x的元素的下标,若不存在,则输出-1.
解析:
这是一道典型的预处理优化的题目,我们可以先将结果离线处理,并且保存在另外一个重新申请的数组中,然后再一次性输出。
由于是输出任意一个不等于x的元素的下标,因此我们可以通过区间的边界入手,一开始我们可以统计好,在给定数组中,每个数右边第一个不等于该数的元素的下标,然后存储起来,但是需要注意的问题是,如果我们正序统计的话,必然会有很多重复的操作,这样的话,在大量数据的情况下可能会超时例如:
第一个数是1,第二个数也是1,那么从第二个数找到其右边第一个不等于该数的元素的下标就是重复遍历的。
因此我们想到逆序来寻找,这样的话当我们从最后一个数开始寻找的时候,如果前一个数和后一个数相等,那么它们右边第一个不等于该数的元素的下标是相同的。
然后离线处理完毕后,比较a[l]和x,如果不等,则直接输出l,否则的话,则是a[l]==x了,那么这样的话,我们就用预处理好的结果,通过a[l]右边第一个不等于该数的元素的下标与区间的右端点的比较,如果该元素下标大于区间右端点的值,则表示“越界”,则输出-1,反之则输出该元素下标。
需要注意的是:数组最后一个元素右边的数不存在,所以要在预处理的数组中,将b[n]预置成一个很大的数字(至少要大于数组a中元素下标的最大值)
完整代码实现:
#include<cstdio>
#include<algorithm>
const int maxn = 2e5;
const int INF = 0x3f3f3f3f;
int a[maxn+20],b[maxn+20];
int main()
{
int n,m;
while(scanf("%d %d",&n,&m) == 2 && n && m)
{
int l,r,x;
for(int i = 1;i <= n;i++)
scanf("%d",&a[i]);
b[n] = INF; //最右边的数的右边不存在与其不等的数
for(int i = n - 1;i>=1;i--)
{
if(a[i+1]!=a[i]) b[i] = i+1;
else b[i] = b[i+1];
}
while(m--)
{
scanf("%d %d %d",&l,&r,&x);
if(a[l] != x) printf("%d\n",l);
else
{
printf("%d\n",b[l] <= r ? b[l] :-1);
}
}
}
return 0;
}
D题:
题目描述:
You have array a that contains all integers from 1 to n twice. You can arbitrary permute any numbers in a.
Let number i be in positions xi, yi (xi < yi) in the permuted array a. Let's define the value di = yi - xi — the distance between the positions of the number i. Permute the numbers in array a to minimize the value of the sum .
The only line contains integer n (1 ≤ n ≤ 5·105).
Print 2n integers — the permuted array a that minimizes the value of the sum s.
2
1 1 2 2
1
1 1
给定一个数n,然后输出一个数列,数列中的元素包括所有的从1-n的数两次,数列中的数可以任意排列,但是要满足.,使得s的值最小。
解析:
这是一道构造题,百度百科对于构造法的描述:
首先我们从问题入手,既然要使得s的值最小,那么我们便从表达式入手,发现s最小值可以为0,需满足以下两种情况:
1.n的值与i相等
2.di + i - n = 0,即di应该等于n-i;
所以我们可以根据以上的要求,枚举出前五项的数列:
当n=1时,数列为 1 1
当n=2时,数列为 1 1 2 2
当n=3时,数列为 1 3 1 2 2 3
当n=4时,数列为 1 3 3 1 2 4 2 4
当n=5时,数列为 1 3 5 3 1 2 4 4 2 5
当枚举到第五项的时候,规律已经很明显了,因此我们可以分奇偶的情况得出以下完整代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 5e5+5;
int num[maxn<<1];
int main()
{
int n;
while(scanf("%d",&n)==1&&n)
{
int j = 1;
num[2*n] = n; //无论奇偶,最后一个数均为最大的数
if(n%2!=0) //n为奇数时
{
for(int i = 1;i <= n;i=i+2,j++)
num[j] = i;
for(int i = n-2;i >=1;i=i-2,j++)
num[j] = i;
for(int i = 2;i <=n-1;i=i+2,j++)
num[j] = i;
for(int i = n-1;i >=2;i=i-2,j++)
num[j] = i;
}
else
{
for(int i = 1;i <= n-1;i=i+2,j++)
num[j] = i;
for(int i = n-1;i >=1;i=i-2,j++)
num[j] = i;
for(int i = 2;i <=n;i=i+2,j++)
num[j] = i;
for(int i = n-2;i >=2;i=i-2,j++)
num[j] = i;
}
for(int i = 1;i <=2*n;i++)
{
if(i==1) printf("%d",num[i]);
else printf(" %d",num[i]);
}
printf("\n");
}
return 0;
}
本次总结:这次Educational round是数学场,几道题目基本上都是数学思维去做的,但是作为数学题,需要把握一个很重要的东西,那就是把实际问题转换成数学模型去求解,还有就是要自己观察出给定题意的规律,就像是本次比赛的D题,输出数列的结果可能有多种,但是我们要的是能够得出一般规律的数列,所以我们在有多种输出的情况下,找出最有规律或者说与上下结合紧密的那一组,这样的话更有利于我们找出一般性的规律
多练习,多思考。(C题,D题均为好题)
如有错误,还请指正,O(∩_∩)O谢谢