【CCF-CSP】202112-2-序列查询新解100分(读过必懂)

文章目录:

  • 故事的开头总是极尽温柔,故事会一直温柔……
  • 一、代码如下:
  • 二、解题思路
      • 1、观察性质
      • 2、求解步骤
          • (1)、题目的暗示
          • (2)、如何分段
          • (3)、求和
      • 3、提交结果
  • 三、其他思路
  • 四、题目如下:

故事的开头总是极尽温柔,故事会一直温柔……

✨你好啊,我是“ 怪& ”,是一名在校大学生哦。
主页链接:怪&的个人博客主页
☀️博文主更方向为:课程学习知识、作业题解、期末备考。随着专业的深入会越来越广哦…一起期待。
❤️一个“不想让我曾没有做好的也成为你的遗憾”的博主。
很高兴与你相遇,一起加油!

一、代码如下:

#include 
#include  //绝对值函数abs()头文件
using namespace std;
#define num 100002

int n=0;//序列有n个数 
int N=0;//N的值 
int a[num]={0};//序列最多有1*10^5个数 
int  r=0;// r=N/(n+1) 
int Long=0; 
long long sum=0;

void input(){//输入 
	cin>>n>>N;
	for(int i=1;i<=n;i++){
		cin>>a[i]; 
	} 
} 


long long g(int x){//计算g(x)的值 
	return x/r;	
} 

int main(){
	input();
	r=N/(n+1);	//cout<
	a[n+1]=N;
	a[0]=0;
	//Long=r;
	for(int i=1;i<=n+1;i++){//以f(i)为区域划分计算 
		long long sum1=0;//记录此小区间差值的和 
		for(int j=a[i-1];j<=a[i]-1;j=j+Long){//此区间内有Long个g取值为g(j)的数 
			int NumEnd=(g(j)+1)*r-1;//g取值为g(j)最大的数为NumEnd
			if(NumEnd>a[i]-1) NumEnd=a[i]-1;//上界超出范围,变为区间最上界 
			int NumLong=NumEnd-j+1;//取值为g(i)的数为NumLong个
			long long f_g=abs(i-1-g(j));//f与g差值,f值恒为i-1 
			sum1=sum1+f_g*NumLong;
			Long=NumLong;
		}
		sum=sum+sum1;			
	}
	
	cout<<sum<<endl;
	return 0;
} 

二、解题思路

1、观察性质

f(x)定义:序列A中小于等于x的整数里的最大的数的下标。
g(x)计算方式:r=N/(n+1)   g(x)=x/r
可得f(x)、g(x)分布皆为公差为1的递增序列
示例给的图很直观的表现了此规律:
【CCF-CSP】202112-2-序列查询新解100分(读过必懂)_第1张图片

2、求解步骤

(1)、题目的暗示

CSP的第一、二题联系紧密,这次的第一题中已经给出了暗示:分段!
如果忘了第一题讲是什么?可以看我的这篇文章:【CCF-CSP】202112-1-序列查询100分
其中详细分析了题意、解题思路并附上了题目截图。

(2)、如何分段

先对f分段:for(int i=1;i<=n+1;i++) //以f(i)为区域划分计算
在此区域内f的取值相同,值为:i-1
再对每个f值相同的区域按照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取值相同的区间对齐
所以每次都要计算此Long:Long=NumLong=NumEnd-j+1;
注:由于此特性,所以g取值的上界可能超出了此区间(按f取值相同来划分的区间)的上界
所以需要此语句:if(NumEnd>a[i]-1) NumEnd=a[i]-1;//上界超出范围,变为区间最上界,来将此情况的bug修复。

(3)、求和

上述操作先按f取值相同分段,再按g值相同分段,所以在最底层的段中f值与g值皆恒定,即两者的差值也恒定:以差值乘以数量即可,最后将所有区间计算量加起来即可。

3、提交结果

请添加图片描述

三、其他思路

再附上一个代码,案例能对,但是0分,数据太大爆了,有兴趣的可以看看思路。

#include 

using namespace std;
#define num 100002

int n=0;//序列有n个数 
int N=0;//N的值 
int long a[num]={0};//序列最多有1*10^5个数 
int long r=0;// r=N/(n+1) 
long long sum=0;

void input(){//输入 
	cin>>n>>N;
	for(int i=1;i<=n;i++){
		cin>>a[i]; 
	} 
} 

void output(){//输入,检验输入是否正确 
	cout<<n<<" "<<N<<endl;
	a[0]=0;
	for(int i=1;i<=n;i++){
		cout<<a[i]<<" ";
	}	
	cout<<endl;
} 


long long g(int x){//计算g(x)的值 
	return x/r;	
} 

long long	GSum(long long x,long long  y){//求x到y间,g(i)的和 
	long long gsum=0;
	if(g(x)==g(y)){
		gsum=g(x)*(y-x+1);//相同的g()值共y-x+1个 
	}
	else{
		long long xTop=(x%r)*g(x);//此区间内不包括前(x%r)个g(x) 
		long long yTop=(y%r+1)*g(y);//此区间内包含(y%r+1)个g(y);
		//r组相同的等差数列,首项为g(x),末项为g(y)-1,共 g(y)-1-g(x)+1项 
		gsum=r*((g(x)+g(y)-1)/2*(g(y)-1-g(x)+1)); 
		gsum=gsum-xTop+yTop;//修正sum,多加的xTop需减去,少加的yTop需加上	
	}
	return gsum; 
}


int main(){
	input();
	r=N/(n+1);	//cout<
	a[n+1]=N;
	for(int i=1;i<=n+1;i++){//以f(i)为区域划分计算 
		
		long long fFlag=i-1;//记录此区间的f(i)值 
		long long LeftFlag=a[i-1]; //区间左边界 
		long long RightFlag=a[i]-1;//区间右边界 
		long long LongFlag=RightFlag-LeftFlag+1;//区间长度 
		//i在LeftFlag与RightFlag中,f(i)取值相同
		
		if(LongFlag==1){//区间仅一个长度 
			long long m1=fFlag-g(LeftFlag);
			if(m1<0) m1=(-1)*m1;
			sum=sum+m1;
		} 
		else if(g(LeftFlag)>=fFlag){//在此区间内,g(i)恒大于等于f(i),f(i)恒=i-1 
			sum=sum+GSum(LeftFlag,RightFlag)-fFlag*LongFlag;
		}
		else if(g(RightFlag)<=fFlag){//在此区间内,g(i)恒小于等于f(i),f(i)恒=i-1 
			sum=sum+fFlag*LongFlag-GSum(LeftFlag,RightFlag);
		} 
		else{//在此区间内,g(i)先<=f(i),后>f(i) 
			int WhereFlag=0;//记录g(i)>f(i)的左临界
			for(int j=LeftFlag;j<=RightFlag;j++){
				if(g(j)>fFlag){
					WhereFlag=j;
					break;//跳出 
				}
			}
			long long LeftLong=WhereFlag-LeftFlag;//g(i)<=f(i)部分长度 
			long long RightLong=RightFlag-WhereFlag+1;//g(i)>f(i)部分长度 
			long long sum1=fFlag*LeftLong-GSum(LeftFlag,WhereFlag-1);
			long long sum2= GSum(WhereFlag,RightFlag)-fFlag*RightLong;
			sum=sum+sum1+sum2; 
		}
		//cout<
	}
	cout<<sum<<endl;
	return 0;
} 

四、题目如下:

【CCF-CSP】202112-2-序列查询新解100分(读过必懂)_第2张图片

你可能感兴趣的:(CCF-CSP,算法,c++,ccf,csp)