CSP202112_2
CSP202112_2
题目
思路
暴力
直接枚举[0, n - 1] 所有 k,二分查询在序列中的位置,进行计算。
时间复杂度\(O(Nlogn)\),部分点TLE,实测70pts
区间处理
观察数据,N 为 1e9 的数量级。可以想到不论怎么优化,即使只用遍历一次也是\(O(N)\)的复杂度,不可能过全部点。
而结合前一题的思路提示,针对划分出的区间直接求解,似乎为该题的可行思路。
-
f
求其在某个区间的和,可以直接借鉴第一题方法。在 \(x \in [num[i - 1], num[i])\) 时,所有的 f 都为 i - 1。
-
g
g 在确定的 f 恒等区间里,其未必全部相等。但由于向下取整的运算,g 也存在区间恒等的性质。因此在 f 与 g 的恒等区间重合部分就可以进行直接求解。
for(int i = 1; i <= n + 1; i++) { for(int j = num[i - 1]; j < num[i]; j += len) { int g = j / r; MAX = ((g + 1) * r - 1 < num[i] ? (g + 1) * r - 1 : num[i] - 1); len = MAX - j + 1; ans += (abs(i - 1 - g))*len; } }在上述求解过程,针对当前的 j 确定 g 后,MAX 即为代入求 g 后仍与当前 g 相等的最大 j,以此即可求得对应 g 恒等区间的长度 len。注意 MAX 的运算是可能超出当前 j 的恒等区间的,因此要特判一下。
关于MAX的求解可以直接YY或手写个例子玩一下,偷懒不解释了都求出来后,直接求区间和累加就好了。
Code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x*f;
}
int n, lmt, MAX = -1;
int len;
ll ans;
int num[100010];
int main()
{
n = read(), lmt = read();
for(int i = 1; i <= n; i++)
{
num[i] = read();
//MAX = max(MAX, num[i]);
}
int r = lmt / (n + 1);
//for(int k = 0; k < lmt; k++) //70pts
//{
// int g = k / r;
// int f = upper_bound(num, num + 1 + n, k) - num - 1;
// //cout << f << " ";
// ans += abs(g - f);
//}
num[n + 1] = lmt;
for(int i = 1; i <= n + 1; i++)
{
for(int j = num[i - 1]; j < num[i]; j += len)
{
int g = j / r;
MAX = ((g + 1) * r - 1 < num[i] ? (g + 1) * r - 1 : num[i] - 1);
len = MAX - j + 1;
ans += (abs(i - 1 - g))*len;
}
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号