第一次写的时候直接做的,70分运行超时,最近几次的第二题都是这个套路,坏的很
仔细看样例解释会发现有很多重复的值,是否说明,我们不需要重复算呢?
对于a[i]和a[i-1]之间的x,它们的f(x)都是i-1
对于kr和(k-1)r之间的x,它们的g(x)都是(k-1)
对于两者区间交叉中的差值恒定,区间中整数的个数*差值即可
接下来的难点就是如何获得区间中整数的个数。
基于我说的两条规则,可以画出六种f(x)和g(x)值不变时区间的关系。
N最大为10^9,遍历N就是我第一次写的时候,超时了,那我们只能遍历数组a,移动N的x区间
根据上面6种关系,其中处理方法可以分为两大类。
如果a[i]>kr,那就后移kr直到kr>=a[i]
如果a[i]<=kr,那就继续
代码上有详细注释,建议6种区间关系自己画图看看,很清楚的。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long get_result(){
long long n,N;
cin>>n>>N;
int r=N/(n+1);
long long a[n+2];
//对于a[i]和a[i-1]之间的x,它们的f(x)都是i-1
//对于kr和(k-1)r之间的x,它们的g(x)都是(k-1)
//对于两者区间交叉中的差值恒定,区间中整数的个数*差值即可
long g_x=0;//(k-1)r
long next_x=r;//kr
long now_x=0;//(k-1)r
long long s=0;//结果
a[0]=0;
for(long i=1;i<=n;++i){
cin>>a[i];
}
a[n+1]=N;
for(long i=1;i<=n+1;++i){
//根据两者区间的位置不同,一共六种可能,总结有两大类处理方法
if(next_x>=N){
next_x=N;
}
while(next_x<a[i]){//如果a[i]较大,g_x+=1,next_x+=r
if(a[i-1]<next_x){//如果两者区间相交,则需要计算相交区域的值
int l=min(int(next_x-a[i-1]),int(next_x-now_x));//取小的那个为区间大小
s+=abs((long long)l*(g_x-(i-1)));
}
//往后挪一位
g_x+=1;
next_x+=r;
now_x+=r;
if(now_x>=N){
return s;
}
}
//此时一定是next_x较大,这时直接算
//这种情况下有三种可能
if(g_x<a[i]){//如果两者区域相交,则需要计算相交区的值
int l=min(int(a[i]-a[i-1]),int(a[i]-now_x));//取小的那个为区间大小
s+=abs((long long)l*(g_x-(i-1)));
}
}
return s;
}
int main(){
cout<<get_result()<<endl;
}