CCF- CSP202112-2序列查询新解 简单分段+满分题解

CCF- CSP202112-2序列查询新解 简单分段+满分题解

题目链接:202112-2序列查询新解

思路:

  • 数据最大范围为109,因此需要采用long long类型
  • 分析样例序列A,我们可以对序列A进行预处理:设置a[0]=0,a[n+1]=N
  • 为降低时间复杂度,我们选择遍历序列A,对序列A中的区间进行分段处理
  • 分别枚举序列A的每个段,选定左右端点,左端点为a[i-1],右端点为a[i]-1,注意第i个段的f(i)值为i-1
  • 首先判断左右端点的g(i)值,如果值相等,将两者直接归为一个段,长度为right-left+1;如果值不等,说明该段内的g(i)值不相等,我们需要对该段再次分段,将g(i)值相等的点分为一个段
  • 当再次分段时,我们需要优先处理端点值,将端点值处理为可以整除r的值,便于后续计算
  • 处理左端点时,如果left%r!=0我们需要+(r-left%r)%r,注意%r,因为当前左端点可能刚好可整除r,此时只需要+0;比如5%3=2,我们需要将5更新为6,则需要+2;
  • 处理右端点时,我们需要-right%r;比如5%3=2,我们需要将5更新为3,则需要-2
  • 参考样例3,我们需要对右端点进行特判,当其可以整除r时,不应该放在当前段内,对该点进行单独处理:sum+=labs(r__/r-i+1);
  • 处理完该段的左右端点后,我们得到规整的段,即左端点和右端点都可以整除r,便于将该段分成更小的单位段,单位段的段长为r

具体代码如下:

#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long  LL;
const int M = 1e5+10;
LL a[M];
LL n,N;
int main()
{
    cin>>n>>N;
    LL r = N/(n+1);//得到r值
    //输入序列A,a[0]默认为0
    for(int i=1;i<=n;i++)
        cin>>a[i];
    a[++n]=N;//将N加入到序列A中
    
    LL sum = 0;
    for(int i=1;i<=n;i++)
    {
        LL left = a[i-1];//该段区间的左端点
        LL right = a[i]-1;//该段区间的右端点,注意-1
        
        LL l_ = left/r;//判断左右两端点之间的距离,决定计算方式
        LL r_ = right/r;
        //如果左端点与有端点除以r的值相等,即该段区间的g(i)相等
        if(l_==r_)
        {
            //当前区间的长度为right-left+1;
            //由于g(i)值相等,f(i)值相等,则可以直接求解该段区间的sum
            //注意第i段区间的,f(i)值为i-1
            sum+=labs(l_-i+1)*(right-left+1);
        }
        //如果不想等,说明该区间内g(i)的值不想等,需要再次分段
        else
        {
            
            sum+=(r-left%r)%r*labs(l_-i+1);//处理左边部分,当left%r!=0时,我们优先处理该部分
            sum+=(right%r)*labs(r_-i+1);//同理处理右边部分,当right%r!=0时,我们优先处理该部分,使得剩下的区间端点都可以整除r
            LL l__ = left+(r-left%r)%r;//更新左端点,比如5%3=2,我们需要将5更新为6,则需要+2,即+(r-left%r)%r,注意%r,因为当前端点可能刚好可整除r
            LL r__ = right - right%r;//更新右端点,比如5%3=2,我们需要将5更新为3,则需要-2,直接 -right%r
            //参考样例3,我们需要对右端点进行特判,当其可以整除r时,不应该放在当前段内
            if(r__%r==0)
            {
                sum+=labs(r__/r-i+1);//对最右一个端点进行特判
            }
            //将该区间分为r长度的单位段,进行处理,只需要满足<=r__-1
            while(l__<=r__-1)
            {
                sum+=r*labs(l__/r-i+1);//每个单位段的长度为r
                l__+=r;//跳到下一个单位段
                
            }
        }
    }
    cout<<sum<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值