第24次CSP认证序列查询新解题解(非常详细)

http://118.190.20.162/view.page?gpid=T137
在这里插入图片描述在这里插入图片描述
思路很简单,细节调整很麻烦。

一看数据量,1s肯定复杂度是1e5,要么遍历g(i),要么遍历f(i),让一个去适应另一个。
首先看遍历g(i),让f(i)去适应的情况。
如果遍历g(i),给定一个g(i)的值,会对应一个i的区间,我们要算出区间的首尾的i,记为posl,posr。g(posl)==g(posr),而我们要算出f(posl)和f(posr),此处需要二分查找一下,因此不方便。显然f(posl)<f(posr)。如果f(posr)<=g(posl),说明整个区间内f(i)均小于g(i)。反之如果f(posl)>=g(posl),说明整个区间内f(i)均大于g(i)。这样只需对f(i)和g(i)分别区间求和,然后做差,求绝对值即可。区间求和用前缀和即可。但是由于f(i)的前缀和较为难求,因此我最终采用的是遍历f(i)让g(i)去适应的方式,因为g(i)规律性强更容易求前缀和。还有一种情况是f(posl)<g(posl)<f(posr),由于两个函数均为增函数,因此出现相交的概率较低,直接暴力求出区间内每一个abs(f(i)-g(i))即可。
因为给定i求f(i)和f(i)的前缀和都不太方便,因此我采取了第二种思路。总体和上述思路相同。但是如果是遍历f(i),则区间内任意i的值都是f(i),通过i来求g(i)和g(i)的前缀和都很方便,因为g(i)规律性强,类似于等差数列。
所以最终的思路为:
遍历f(i),让g(i)去适应。遍历f的每一个函数值,而每一个函数值都对应一个区间[posl,posr],在区间内当然每一个f(i)都相等,即f(posl)==f(i)==f(posr)。之后要对f(i)和g(i)分别区间求和,分为两种情况,第一种是f(i)函数值均大于g(i)函数值,或者g(i)函数值均大于f(i)函数值。因为g(posl)<=g(posr),所以只需判断g函数两个端点即可。如果g(posr)<=f(i),则g函数全部小于f,如果g(posl)>=f(i),则g函数全部大于f。这两种情况只需分别区间求和然后取绝对值即可。还有一种情况为g(posl)<f(i)<g(posr),这种情况较为少见,直接暴力求出,即对每一个i求|f(i)-g(i)|。
求g函数前缀和只需要处理一下边界,然后直接等差数列求和然后再乘r即可。
注意点:
1.f函数开始默认为0 ,因此需要数组先补0.
2.f函数最后一位需要补N,处理边界问题。
3.全开long long,当时因为没看见一个地方爆longlong调了好久,还不如直接全开long long。
4.前缀和如果下标小于0的是0

#include <bits/stdc++.h>
typedef long long ll;
const ll maxn=100000;
using namespace std;
ll n,N,r;
ll a[maxn+10];
inline ll g(ll pos){
    return pos/r;
}
inline ll sumg(ll pos){
    if(pos<0)return 0;
    else if((pos+1)%r==0)return (pos/r+1)*(pos/r)/2*r;
    else return (pos/r+1)*(pos/r)/2*r-(r-(pos+1)%r)*g(pos);
}
int main()
{
    cin>>n>>N;
    r=N/(n+1);
    a[0]=0;
    for(ll i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    a[n+1]=N;
    ll sum=0;
    for(ll i=0;i<=n;i++){
        ll lpos=a[i],rpos=a[i+1]-1;
        ll gl=g(lpos),gr=g(rpos);
        //printf("pos:(%d ,%d)\n",lpos,rpos);
        ll fl=i;
        if(fl<=gl||fl>=gr){
            ll tsumg=ll(sumg(rpos)-sumg(lpos-1));
            //printf("(sumgr,sumgl)%lld %lld\n",sumg(rpos),sumg(lpos-1));
            ll tsumf=ll(fl*(rpos-lpos+1));
            ll tsum=tsumg-tsumf;
            sum+=llabs(tsum);
            //printf("(%lld,%lld):%lld\n",tsumf,tsumg,tsum);
        }
        else{
            ll tsum=0;
            for(ll j=lpos;j<=rpos;j++){
                tsum+=llabs(g(j)-fl);
            }
            sum+=tsum;
            //printf("*%lld*\n",tsum);
        }
        //printf("sum:%lld\n",sum);
    }
    printf("%lld\n",sum);
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值