51nod 1617 奇偶数组 分治

题意

a是一个包含n个元素的数组。对a中的元素进行1-n编号。
定义“偶数组” even, eveni=a2i(1≤2i≤n) ,即“偶数组” even是由数组a中编号为偶数的元素组成的。
定义“奇数组” odd, eveni=a2i−1(1≤2i−1≤n) ,即“奇数组”odd是由数组a中编号为奇数的元素组成的。

然后,我们定义一个转换方程F(a),F(a)的结果为一个数组,过程如下:

当n>1时,F(a)=F(odd)+F(even),
其中“+”是合并的意思(如[1,3]+[2,4]=[1,3,2,4]),odd和even为前面所描述的数组。

当n=1时,F(a)=a。

a的初始值为n个数,元素的值为1,2,3……n。
b为a经过变换后的数组,即b=F(a)。题目将会给出m个查询(l,r,u,v)。
你的任务是对b中第l个到第r个元素(含),并且元素的值在[u,v]区间内的元素进行求和,并对mod取模。
用公式表示如下: (∑u ≤ bi ≤ v && l ≤ i ≤ rbi) % mod 。

样例解释:
b=F(a )=F([1,2,3,4])
第1步,F([1,2,3,4])= F([1,3])+ F([2,4])
第2步,F([1,3])= F([1])+ F([3])=[1]+[3]=[1,3]
第3步,F([2,4])= F([2])+ F([4])=[2]+[4]= [2,4]
第4步,b=F(a )=F([1,2,3,4])= F([1,3])+ F([2,4])= [1,3]+ [2,4]=[1,3,2,4]

所以b= [1,3,2,4].
对于第1个查询,l=2,r=3,u=4,v=5。第2,3个位置上的元素是3,2,都不在区间[4,5]内,所以结果为0。
对于第2个查询,l=2,r=4,u=1,v=3。其中第2,3个位置上的元素是3,2,刚好在区间[1,3]内,所以结果是5。
1≤n≤10^18,1≤m≤10^5,1≤mod≤10^9,1≤l≤r≤n,1≤u≤v≤10^18

分析

初始时刻显然该数列是公差为1的等差数列。将其按奇偶性分开后,显然每个数列均为公差2的等差数列。
我们定义solve(x,l,r,d,fir)函数表示求首项为fir,公差为d且最后一项不大于n的等差数列变换后的前x项位于[l,r]中的数的和。
如果x大于该数列的奇数项个数,那么就O(1)算出所有奇数项的和,然后递归计算solve(x-奇数项个数,l,r,d*2,fir+d),否则就递归计算solve(x,l,r,d*2,fir)即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

int m,MOD;
LL n;

LL readLL()
{
    LL x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

LL mul(LL x,LL y,LL MOD)
{
    return (x%MOD)*(y%MOD)%MOD;
}

LL get(LL d,LL fir,LL l)
{
    if (l<fir) return 0;
    LL r=fir+(l-fir)/d*d,s=((r-fir)/d+1)%MOD;
    return (r+fir)%2==0?mul((r+fir)/2,s,MOD):mul(r+fir,s/2,MOD);
}

LL solve(LL x,LL l,LL r,LL d,LL fir)
{
    if (!x) return 0;
    LL s=(n-fir)/d+1;
    if (s==1) return fir>=l&&fir<=r?fir%MOD:0;
    if (x<=(s+1)/2) return solve(x,l,r,d*2,fir);
    else return (solve(x-(s+1)/2,l,r,d*2,fir+d)+get(d*2,fir,r)-get(d*2,fir,l-1)+MOD)%MOD;
}

int main()
{
    scanf("%lld%d%d",&n,&m,&MOD);
    while (m--)
    {
        LL l=readLL(),r=readLL(),u=readLL(),v=readLL();
        if (u>n) puts("0");
        else v=min(v,n),printf("%lld\n",(solve(r,u,v,1,1)-solve(l-1,u,v,1,1)+MOD)%MOD);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值