CCF 202112-2 序列查询新解 python 满分
题目叙述
问题描述:略
输入格式:略
输出格式:略
样例
样例1输入
3 10
2 5 8
样例1输出
5
样例2输入
9 10
1 2 3 4 5 6 7 8 9
样例2输出
0
样例3输入
2 10
1 3
样例3输出
6
补充样例4输入
3 6
1 3 5
补充样例4输出
6
补充样例5输入
5 9
1 2 5 6 7
补充样例5输出
12
满分证明
解题思路
说实话,这题我做了好久才自己码出答案,确实有太多需要注意的地方。
听我室友讲现在CCF将Python运行时间和C++运行时间的评分标准调整了,也就是说同样的逻辑因为C++用的是底层代码所以运行速度更快,可以拿满分;但因为Python是再开发语言,运行速度慢,不能拿满分,这也解释了为什么第一题同样逻辑只有C++可以拿满分。
言归正传,在默认第一题拿到满分的情况下做第二题(第一题 CCF 202112-1 序列查询 python 满分),明白了需要分段的思路,第二题才有可能有思路。
01Python超时70分
01代码的思路很简单,优先创建两个数组,然后使用一次遍历就可以;这个代码中理论上应该把代码中的1e7 + 5换成1e9 + 5。如果使用C++语言,就可以得到满分(似乎之前看到有博主写了,所以这里我就不贴C++代码了)。但是如果你这里使用的Python,使用1e9 + 5会显示超时0分,直到你改为1e7 + 5,所以如果想用Pytho得满分就得进行优化。
02满分python思路
其实我和大佬满分思路一样(哈哈哈,准确说是我“借鉴”的大佬的参考博文1)。细心的看官应该注意到我在测试样例中多给出两个测试样例,正是因为这两个测试样例才让我的代码进一步修改为满分。
刚开始我只注意到在f(x)区间上分段,计算给定i(索引)下前g(x)的和,然后f(x)段和减去下面g(x)的和就可以了;事实上是我想太少了。因为在同样f(x)段下依然不能简单的减去g(x)的分段和;比如
f(x) | 1 | 1 | 1 |
---|---|---|---|
g(x) | 0 | 1 | 2 |
|g(x)-f(x)| | 1 | 0 | 1 |
如果按之前的思路,则计算结果为0,但实际结果为2; | |||
这里里面涉及到的就是如果在同一段f(x)下,如果g(x)都小于f(x)或都大于f(x)的话可以直接相减,但如果有“转折点”的话就要再分情况计算了。所以这道题我认为有三个难点。 |
第一,计算前n项g(x)的和
这里我们可以看作是r(N//(n+1))个首项为0,公差为1的等差数列;等差数列求和公式Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/2,这里不过多赘述自己推导即可(r个完整的等差数列求和再加最后几个不完整的);如果还不太懂可以看参考博文2,我最后推导完后事前i项和:
x = (i + 1) // r
sum_g = (x * (x - 1) * r / 2) + ((i + 1) % r) * x
第二,判断是否存在转折点条件
这里要判断要么区间内值全小于等于f(i),要么区间内值全大于等于f(i),这里f(i)实际就是i-1;这里等于放不放没影响,递增函数,同时满足这两个条件,有点像做高中数学函数恒成立问题;这里也不过多赘述。
第三,判断完转折点后如何
如果没有转折点,直接f(x)区间和减g(x)区间和;如果有转折点,找出转折点,从转折点处分段求。
具体实现见代码部分,为了更好地让大家理解题意和使用测试样例,我将所有样例的f(x), g(x), |f(x)-g(x)|均以列表的形式展示给大家,方便大家自检。
样例详细描述
样例1描述
n=3 N=10 r=2 out=5
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 | 3 |
g(i) | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 |
|g(i)-f(i)| | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
样例2描述
n=2 N=10 r=3 out=6
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
g(i) | 0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 |
|g(i)-f(i)| | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
样例3描述
n=2 N=10 r=2 out=5
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 |
g(i) | 0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 |
|g(i)-f(i)| | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
补充样例4描述
n=3 N=6 r=1 out=6
i | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
f(i) | 0 | 1 | 1 | 2 | 2 | 3 |
g(i) | 0 | 1 | 2 | 3 | 4 | 5 |
|g(i)-f(i)| | 0 | 0 | 1 | 1 | 2 | 2 |
补充样例5描述
n=5 N=9 r=1 out=12
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 1 | 2 | 2 | 2 | 3 | 4 | 5 | 5 |
g(i) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|g(i)-f(i)| | 0 | 0 | 0 | 1 | 2 | 2 | 2 | 2 | 3 |
具体实现代码
01Python超时70分代码
n, N = map(int, input().split())
ori_li = list(map(int, input().split()))
f = [0 for _ in range(int(1e7 + 5))]
dif = [0 for _ in range(int(1e7 + 5))]
for i in ori_li:
dif[i] = dif[i] + 1
sum_f = 0
index = 1
fx = 0
r = (N // (n + 1))
for i in range(1, N):
f[i] = f[i - 1] + dif[i]
sum_f += abs(f[i] - (i // r))
print(sum_f)
02我的Python满分代码
n, N = map(int, input().split())
ori_li = list(map(int, input().split()))
ori_li.insert(0, 0)
sum_ = 0
r = (N // (n + 1))
# 计算g(x)分段和
def dif(id_f, id_s, r):
id_fx = (id_f + 1) // r
id_sx = (id_s + 1) // r
sum_gs = (id_sx * (id_sx - 1) * r / 2) + ((id_s + 1) % r) * id_sx
sum_gf = (id_fx * (id_fx - 1) * r / 2) + ((id_f + 1) % r) * id_fx
return sum_gs - sum_gf
for i in range(1, n + 1):
sum_f = (ori_li[i] - ori_li[i - 1]) * (i - 1)
id_f = ori_li[i - 1] - 1
id_s = ori_li[i] - 1
if ((id_s // r) > (i - 1)) and ((id_f // r) < (i - 1)):
id_m = i * r - 1
sum_gf = dif(id_f, id_m, r)
sum_ = sum_ + abs(sum_f - (i - 1) * (id_s - id_m) - sum_gf)
sum_gs = dif(id_m, id_s, r)
sum_ = sum_ + abs(sum_f - (i - 1) * (id_m - id_f) - sum_gs)
else:
sum_g = dif(id_f, id_s, r)
sum_ = sum_ + abs(sum_f - sum_g)
# 处理结尾
if ori_li[-1] <= N - 1:
sum_f = (N - ori_li[-1]) * n
id_s = N - 1
id_f = ori_li[-1] - 1
if ((id_s // r) > n) and ((id_f // r) < n):
id_m = n * r + 1
sum_gf = dif(id_f, id_m, r)
sum_gs = dif(id_m, id_s, r)
sum_ = sum_ + abs(sum_f - n * (id_s - id_m) - sum_gf)
sum_ = sum_ + abs(sum_f - n * (id_m - id_f) - sum_gs)
else:
sum_g = dif(id_f, id_s, r)
sum_ = sum_ + abs(sum_f - sum_g)
print(int(sum_))
03大佬Python满分代码
n, N = tuple(map(int, input().split()))
# 有用的点
A = [0] + [int(x) for x in input().split()]
# 计算gi数列下标到x的和
def h(x):
if x <= 0:
return 0
global r
x = x + 1
n = x // r
m = x % r
return int(n * (r * (n - 1) / 2 + m))
# 对下标为left到right的abs(fi-gi)求和,包括left和right两点
def cal(fi, left, right):
return abs(h(right) - h(left - 1) - fi * (right - left + 1))
# 判断是否有转折点,并返回error增加量
def ischange(fi, left, right):
# 没有转折点
if left // r >= fi or right // r <= fi:
return cal(fi, left, right);
# 出现转折点
else:
# 算出第一个转折点
splitindex = left + r * (fi - (left + 1) // r) + r - (left + 1) % r
return cal(fi, left, splitindex) + ischange(fi, splitindex + 1, right)
r = N // (n + 1)
error = 0
# 根据fi的数值来分组,开始遍历
for fi in range(n + 1):
left = A[fi]
if fi < n:
right = A[fi + 1] - 1
else:
right = N - 1
error += ischange(fi, left, right)
print(error)
感谢及参考博文
部分内容参考以下链接,这里表示感谢 Thanks♪(・ω・)ノ
参考博文1 序列查询新解(python满分解法)
https://blog.youkuaiyun.com/qq_47780707/article/details/123262314
参考博文2 CCF CSP 202112-2 序列查询新解
https://blog.youkuaiyun.com/reidpa/article/details/121942425
参考博文3 202112(第24次)CSP真题202112-1,2讲解
https://www.bilibili.com/video/BV1v34y117Vi
参考博文4 【CCF-CSP】202112-2-序列查询新解100分(读过必懂)
https://blog.youkuaiyun.com/qq_21471309/article/details/123296864
需者自取传送门(∩ᄑ_ᄑ)⊃━☆【CCF 2013-2021】本博主整理历年至少前两题 python 满分代码目录