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;
}
posted @ 2022-09-23 21:01  Kevin_Chance  阅读(81)  评论(0)    收藏  举报