题在洛谷里面洛谷居然没有题解,让我硬生生错了好几次!
很明显这个序列是有规律的
for(ll i=1;i
同时每一项的和又代表一共有多少个数,比如
{1},{1,2},{1,2,3}
sum{1,2,3}=6 刚好一共是6个数
输入的li和ri是给定数的位置,问[li,ri]的区间和是多少
所以考虑使用前缀和
所以我们要找到所给数的位置,这样就可以利用前缀和快速求出答案。
毫无疑问可以用
int i;
for(i=1;i<=MAXN;i++){
if(a[i]>li)break;
}
i--;
来找出li的位置,必定是小于a[i]的
假设最后一部分li的落点{1,2,3,4,5,...,n}在5上面
i li
那么答案就是s[i]+a[li-a[i]]
一个for找的太慢了,发现是线性,所以用二分来定位置
inline ll query(ll i){
//确定第几项开始
ll l=0,r=MAXN;
while(l>1;
if(a[mid]<=i)//位置太小了,往后找
l=mid;
else//往前
r=mid;
}
return s[l]+a[i-a[l]];
}
因为内部没有+1,所以l==r-1就是最终我们需要的位置
AC代码
#include
using namespace std;
using ll=long long;
const ll M=1414215;
int t;
ll l1,r1,a[M],s[M];
ll questsum(ll i){
ll l=0,r=M+1;
while((l+1)!=r){
ll mid=(l+r)>>1;
if(a[mid]<=i){
l=mid;
}else{
r=mid;
}
}
return(s[l]+a[i-a[l]]);
}
int main(){
scanf("%d",&t);
for(ll i=1;i<=M-1;i++){
a[i]=a[i-1]+i;
}
for(ll i=1;i<=M-1;i++){
s[i]=a[i]+s[i-1];
}
for(int i=1;i<=t;i++){
scanf("%lld%lld",&l1,&r1);
printf("%lld\n",questsum(r1)-questsum(l1-1));
}
return 0;
}
个人感觉手打二分还是比较麻烦的,一要考虑是否要内部要不要+1,循环内是l 不如直接STL的lower_bound,虽然会慢上不少。 手写的话最好自行画图,保证代码正确性 存在l,m,r三者取值的问题就是因为二分的序列可能是一块区间都是正确的,取左端点和右端点就会有区别。