dtoi3031 交错和查询 (sum)

Posted 1124828077ccj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dtoi3031 交错和查询 (sum)相关的知识,希望对你有一定的参考价值。

题意:

     有一个长度很大(10^18)的序列是由长度为n的序列不断循环构成的。长度为n的序列给定且数值均为0~9。每次有两个操作。

     1.修改循环节上的一位。

     2.询问[l,r] 内所有连续子串的交错和的和,1<=l,r<=1e18。一个子串[l,r]的交错和=a[l]-a[l+1]+a[l+2]-...+(-1)r-la[r]。

题解:

     先考虑l,r都小于等于n的情况。考虑对于[l,r]中的一个位置i,它对答案(这里当然指所有连续子串)的贡献。显然,如果i是奇数位,那么贡献就是a[i]*(r-i+1),否则就是0,因为前面一加一减的就抵消了。那么可以使用线段树维护,我们需要维护的值有:奇数位的和,偶数位的和,奇数位的贡献,偶数位的贡献,以及长度。合并的时候分类讨论一下即可。

     那么当l和r很大的时候怎么办呢,其实跟上面的做法一样,只不过现在我们需要维护很多个完整的序列拼在一起的答案。可以使用倍增,合并的方法与线段树节点合并方法一样。最后我们只需要把前一部分+中间一堆重复的完整的序列+末尾一部分的三个部分合并即可求出答案。本题的核心在于合并,因为程序中每一个地方的合并都是相同的方法。

#include<cstdio>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int mod=1e9+7;
int n,m;
char s[200002];
typedef struct{
    long long ans1,ans2,s1,s2,len;
}P;
P p[800002],ans,t[70];
long long read(){
    long long f=0;char ch=getchar();
    while(ch<0 || ch>9)ch=getchar();
    while(ch>=0 && ch<=9){f=f*10+ch-48;ch=getchar();}
    return f;
}
P hb(P a,P b){
    P t;
    if (a.len%2)
    {
        t.ans1=(a.s1*(b.len%mod)%mod+a.ans1+b.ans2);
        t.ans2=(a.s2*(b.len%mod)%mod+a.ans2+b.ans1);
        t.s1=a.s1+b.s2;t.s2=a.s2+b.s1;
    }
    else
    {
        t.ans1=(a.s1*(b.len%mod)%mod+a.ans1+b.ans1);
        t.ans2=(a.s2*(b.len%mod)%mod+a.ans2+b.ans2);
        t.s1=a.s1+b.s1;t.s2=a.s2+b.s2;
    }
    t.ans1%=mod;t.ans2%=mod;t.s1%=mod;t.s2%=mod;
    t.len=a.len+b.len;
    return t;
}
void build(int root,int begin,int end){
    if (begin==end)
    {
        int z=s[begin]-0;
        p[root].ans1=p[root].s1=z;p[root].ans2=p[root].s2=0;p[root].len=1;
        return;
    }
    int mid=(begin+end)/2;
    build(root*2,begin,mid);build(root*2+1,mid+1,end);
    p[root]=hb(p[root*2],p[root*2+1]);
}
void gengxin(int root,int begin,int end,int wz,int z){
    if (begin>wz || end<wz)return;
    if (begin==end)
    {
        p[root].ans1=p[root].s1=z;p[root].ans2=p[root].s2=0;p[root].len=1;
        return;
    }
    int mid=(begin+end)/2;
    gengxin(root*2,begin,mid,wz,z);gengxin(root*2+1,mid+1,end,wz,z);
    p[root]=hb(p[root*2],p[root*2+1]);
}
void chaxun(int root,int begin,int end,int begin2,int end2){
    if (begin>end2 || end<begin2)return;
    if (begin>=begin2 && end<=end2)
    {
        ans=hb(ans,p[root]);
        return;
    }
    int mid=(begin+end)/2;
    chaxun(root*2,begin,mid,begin2,end2);chaxun(root*2+1,mid+1,end,begin2,end2);
}
int main()
{
    scanf("%d%s%d",&n,s+1,&m);
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        int op;
        op=read();
        if (op==1)
        {
            int a,b;
            a=read();b=read();gengxin(1,1,n,a,b);
        }
        else
        {
            long long l,r,a,k;
            l=read();r=read();ans.ans1=ans.ans2=ans.s1=ans.s2=ans.len=0;
            a=l/n*n;l-=a;r-=a;
            if (!l){l+=n;r+=n;}
            if (r<=n)chaxun(1,1,n,l,r);
            else
            {
                k=(r-1)/n-(l-1)/n-1;
                chaxun(1,1,n,l,n);
                t[0]=p[1];
                for (int i=0;i<=60;i++)
                {
                    if (i)t[i]=hb(t[i-1],t[i-1]);
                    if ((1ll<<i)&k)ans=hb(ans,t[i]);
                }
                chaxun(1,1,n,1,(r%n!=0?r%n:n));
            }
            printf("%lld
",ans.ans1);
        }
    }
    return 0;
}

以上是关于dtoi3031 交错和查询 (sum)的主要内容,如果未能解决你的问题,请参考以下文章

练习4-3 求给定精度的简单交错序列部分和(15 分)

1 代码片段1

[PTA]实验4-1-8 求给定精度的简单交错序列部分和

练习2-15 求简单交错序列前N项和(15 分)

dtoi4649 光明

[PTA]实验2-3-4 求简单交错序列前N项和