CCF-CSP 202112-2 序列查询新解

 

 


这里我的第一思路还是延续上一题对f的分段,即将f值相同的点作为一段,然后去计算对应段内的g值之和,然后对f值之和与g值求差的绝对值即可,这里需要注意的是可能存在f值<g值的情况,如果求和再相减会出现减多了的情况,实际上应该的操作是分别求差的绝对值,即实际上应该是加的,那么我们就需要补上减的数*2

#include<iostream>
#include<cmath>
#define LL long long
using namespace std;

int r,N,n;
int A[100005];

int main(){
	int num = 0;
	LL ans = 0;
	cin>>n>>N;
	r=floor((1.0 * N)/(1.0*(n+1)));
	for(int i=1;i<=n;i++){
		cin>>A[i];
	}
	A[n + 1] = N;
	for (int i = 1; i <= n+1; i++) {
		int sum_f = ((A[i] - A[i - 1]) * num++), sum_g = 0;//每一段的f值和
		for (int j = A[i - 1]; j < A[i]; j++) {
			int temp = g(j);
			sum_g += temp;//每一段的g值之和
			if (temp > num - 1) {
				sum_f += (2 * (temp - num + 1));//补上减去的部分
			}
		}
		ans += abs(sum_f - sum_g);
	}
	cout << ans << endl;
	return 0;
}

这里的时间复杂度只足以拿到70分

参考自

CSP的一二题联系紧密,在第一题中实际上已经给出了暗示:分段 

上述思路也的确使用了分段,但是还是不够,我们还要对g值进行分段

即上图例子中,[0,1]、[2,3]、[4,4]、[5,5]、[6,7]、[8,9]为段

因此我们需要在每一个f段内对g进行分段,实际上我们不难发现我们求得的r值就是每r个数的g值都是相等的,利用这一点我们就可以对g值分段了

for(int j=a[i-1];j<=a[i]-1;j=j+Long){//此区间内有Long个g取值为g(j)的数

j值改变条件为:j=j+Long,这个Long为变值,因为==g取值相同的区间不完全与f取值相同的区间对齐==

由于此特性,所以g取值的上界可能超出了此区间(按f取值相同来划分的区间)的上界
所以需要此语句:if(NumEnd>a[i]-1) NumEnd=a[i]-1;//上界超出范围,变为区间最上界 ,来将此情况的bug修复。

最后对每个区间的差值计算后叠加即可

#include<iostream>
#include<cmath>
#define LL long long
using namespace std;

int r, N, n,sizee;
int A[100005];

int g(int x) {
	return floor((1.0 * x) / r);
}

int main() {
	int num = 0;
	LL ans = 0;
	cin >> n >> N;
	r = floor((1.0 * N) / (1.0 * (n + 1)));
	for (int i = 1; i <= n; i++) {
		cin >> A[i];
	}
	A[n + 1] = N;
	for (int i = 1; i <= n + 1; i++) {//对f进行分段
		int sum = 0;
		for (int j = A[i - 1]; j < A[i]; j += sizee) {//对g进行分段
			int endIndex = (g(j) + 1) * r - 1;//当前g分段的最后一个数的下标
			if (endIndex > A[i] - 1) {
				endIndex = A[i] - 1;
			}
			int numGj = endIndex - j + 1;
			LL f_g = abs(i - 1 - g(j));
			sum += (f_g * numGj);
			sizee = numGj;
		}
		ans += sum;
	}
	cout << ans << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值