zoj3813 线段树

比赛的时候想到这个公式,可惜对于中间那部分处理不得当,导致要记录的东西太多,最后不了了之。

规律很好推理,就是从头到尾的s1*b1+s3*b2。。。。b1等于项数,b2等于项数减2,然后就是维护与更新了。

写的时候一开始没将串翻倍,一直WA,估计某个地方爆longlong了,改了好久都WA,怒将串翻倍,搞定。

附代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define delf LL m=(l+r)>>1
#define LL long long int

using namespace std;

const int MAX=200010;
const LL MOD=1000000007;
LL tsum[2][MAX<<2];    //记录每个区间以最左端为起点的前缀和(1当前位开始0当前下一位开始)
LL sum[2][MAX<<2];     //记录偶数项的和和奇数项的和(1当前位开始0当前下一位开始)
char ch[MAX];
LL n;

struct node
{
    LL sum0;
    LL sum1;
    LL tsum0;
    LL tsum1;
    node()
    {
        sum0=sum1=tsum0=tsum1=0;
    }
};

void pushup(LL l,LL r,LL rt)
{
    delf;
    int len=(m-l+1);
    if (len%2==0)
    {
        sum[0][rt]=(sum[0][rt<<1]+sum[0][rt<<1|1])%MOD;
        tsum[0][rt]=(tsum[0][rt<<1]+(r-m)*sum[0][rt<<1]%MOD+tsum[0][rt<<1|1])%MOD;
        sum[1][rt]=(sum[1][rt<<1]+sum[1][rt<<1|1])%MOD;
        tsum[1][rt]=(tsum[1][rt<<1]+(r-m)*sum[1][rt<<1]%MOD+tsum[1][rt<<1|1])%MOD;
    }
    else
    {
        sum[0][rt]=(sum[0][rt<<1]+sum[1][rt<<1|1])%MOD;
        tsum[0][rt]=(tsum[0][rt<<1]+(r-m)*sum[0][rt<<1]%MOD+tsum[1][rt<<1|1])%MOD;
        sum[1][rt]=(sum[1][rt<<1]+sum[0][rt<<1|1])%MOD;
        tsum[1][rt]=(tsum[1][rt<<1]+(r-m)*sum[1][rt<<1]%MOD+tsum[0][rt<<1|1])%MOD;
    }
    return ;
}

void build(LL l,LL r,LL rt)
{
    tsum[0][rt]=tsum[1][rt]=0;
    sum[0][rt]=sum[1][rt]=0;
    if (l==r)
    {
        if (l>n)
            l=l-n;
        LL v=ch[l]-'0';
        sum[1][rt]=v;
        tsum[1][rt]=v;
        return ;
    }
    delf;
    build (lson);
    build (rson);
    pushup(l,r,rt);
    return ;
}

void update(LL k,LL v,LL l,LL r,LL rt)
{
    if (l==r)
    {
        sum[1][rt]=tsum[1][rt]=v;
        return ;
    }
    delf;
    if (k<=m)
        update(k,v,lson);
    else
        update(k,v,rson);
    pushup(l,r,rt);
    return ;
}

node query(LL L,LL R,LL l,LL r,LL rt)
{
    if (L==l&&r==R)
    {
        node s;
        s.sum0=sum[0][rt];
        s.sum1=sum[1][rt];
        s.tsum0=tsum[0][rt];
        s.tsum1=tsum[1][rt];
        return s;
    }
    delf;
    if (R<=m)
        return query(L,R,lson);
    else if (L>m)
        return query(L,R,rson);
    else
    {
        delf;
        node s;
        node s1=query(L,m,lson);
        node s2=query(m+1,R,rson);
        int len=(m-L+1);
        if (len%2==0)
        {
            s.sum0=(s1.sum0+s2.sum0)%MOD;
            s.sum1=(s1.sum1+s2.sum1)%MOD;
            s.tsum0=(s1.tsum0+(R-m)*s1.sum0%MOD+s2.tsum0)%MOD;
            s.tsum1=(s1.tsum1+(R-m)*s1.sum1%MOD+s2.tsum1)%MOD;
        }
        else
        {
            s.sum0=(s1.sum0+s2.sum1)%MOD;
            s.sum1=(s1.sum1+s2.sum0)%MOD;
            s.tsum0=(s1.tsum0+(R-m)*s1.sum0%MOD+s2.tsum1)%MOD;
            s.tsum1=(s1.tsum1+(R-m)*s1.sum1%MOD+s2.tsum0)%MOD;
        }
        return s;
    }
}

LL gets(LL a1,LL d,LL t)
{
    LL s1=a1*(t%MOD)%MOD;
    LL t1=t-1;
    if (t%2)
        t1=t1/2;
    else
        t=t/2;
    t=t%MOD;
    t1=t1%MOD;
    LL s2=(t*t1%MOD)*d%MOD;
    s1=(s1+s2)%MOD;
    return s1;
}

LL solve(LL l,LL r)
{
    LL nn=2*n;
    LL t1=(l-1)/nn+1;       //l处于哪个循环节
    LL t2=(r-1)/nn+1;       //r处于哪个循环节
    LL l1=l%nn;
    LL r1=r%nn;
    if (l1==0)
        l1=nn;
    if (r1==0)
        r1=nn;
    if (t1==t2)
        return query(l1,r1,1,nn,1).tsum1%MOD;
    LL t=(t2-t1-1);         //l所处的循环节和r所处的循环节中间有几个循环节
    LL s;
    node s1=query(l1,nn,1,nn,1);        //前缀部分
    node s2=query(1,r1,1,nn,1);         //后缀部分
    s=(s1.tsum1+(((r-l+1)-(nn-l1+1))%MOD)*s1.sum1)%MOD;
    if ((nn-l1+1)%2==0)
        s=(s+s2.tsum1)%MOD;
    else
        s=(s+s2.tsum0)%MOD;
    //处理中间的循环节部分,最坑点
    if (t>0)
    {
        if ((nn-l1+1)%2==0)  //开始循环前偶数个数
            s=(s+(t%MOD)*tsum[1][1]%MOD+gets(r1,nn,t)*sum[1][1]%MOD)%MOD;
        else                //开始循环前奇数个数
            s=(s+(t%MOD)*tsum[0][1]%MOD+gets(r1,nn,t)*sum[0][1]%MOD)%MOD;
    }
    return s%MOD;
}

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",ch+1);
        n=strlen(ch+1);
        build (1,2*n,1);
        int q;
        scanf("%d",&q);
        while (q--)
        {
            int p;
            LL a,b;
            scanf("%d",&p);
            scanf("%lld%lld",&a,&b);
            if (p==1)
            {
                update(a,b,1,2*n,1);
                update(a+n,b,1,2*n,1);
            }
            else
            {
                LL ans=solve(a,b)%MOD;
                printf("%lld\n",ans);
            }
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值