一、🌳代码如下:
#include <iostream>
#include <cmath> //绝对值函数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<<r<<endl; //检验r的计算
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(j)的数为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的递增序列。
示例给的图很直观的表现了此规律: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值皆恒定,即两者的差值也恒定:以差值乘以数量即可,最后将所有区间计算量加起来即可。
三、其他思路
再附上一个代码,案例能对,但是0分,数据太大爆了,有兴趣的可以看看思路。
#include <iostream>
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<<r<<endl; //检验r的计算
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<<sum<<endl;
}
cout<<sum<<endl;
return 0;
}