ZOJ 3813 Alternating Sum 树状数组

Alternating Sum

Time Limit: 2 Seconds       Memory Limit: 65536 KB

There is a digit string S with infinite length. In addition, S is periodic and it can be formed by concatenating infinite repetitions of a base string P. For example, if P = 3423537, then S = 3423537342353734235373423537...

Let's define the alternating sum on substrings of S. Assume Sl..r is a substring of S from index l to index r (all indexes are 1-based), then the alternating sum of Sl..r is:

G( lr) =  S l -  S l+1 +  S l+2 - ... + (-1) r-l S r

For example, S2..10 = 423537342, then G(2, 10) = 4 - 2 + 3 - 5 + 3 - 7 + 3 - 4 + 2 = -3.

Now, you are given the base string P and you have to do many operations. There are only two kinds of operations:

  • x d: set Px to dd is a single digit.
  • l r: find the sum of G(ij) that l <= i <= j <= r.

For each second operation, you should output the sum modulo 109 + 7.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains a digit string P (1 <= length(P) <= 100000).

The second line contains an integer Q (1 <= Q <= 100000) indicating the number of operations. Each of the following Q lines is an operation in such format:

  • x d (1 <= x <= length(P), 0 <= d <= 9)
  • l r (1 <= l <= r <= 1018)

Output

For each "2 l r" operation, output an integer, indicating the sum modulo 109 + 7.

Sample Input
2
324242
4
2 1 1
2 1 4
1 3 7
2 3 4
324242
6
2 1 1
1 3 7
2 2 4
1 3 4
2 7 10
2 1 30
Sample Output
3
20
14
3
8
20
870

Author:  LIN, Xi

Source: The 2014 ACM-ICPC Asia Mudanjiang Regional First Round


解题思路:对于询问[L,R]只有2种情况:1.在一段中 2.L,R分别在两段中,中间有若干段

第一步:

那么我们先处理原串P的情况。

在纸上写一下,容易发现,G(l,r)只有奇数项

且G(l,r)=a[l]*(r-l+1)+a[l+2]*(r-l-1)+....=sigma(a[x]*(r-x+1))=(r+1)*sigma(a[x])-sigma(a[x]*x)

那么,我们用树状数组维护a[x]和a[x]*x两个前缀和,修改就是单点修改。

但是,偶数项和并不是不用了,如果前面有一段是奇数,后面就要用到偶数项和

所以要维护奇数偶数项前缀的4个数组。


第二步:

我们已经知道原串P的任意一个区间的答案

如果有2个P长度的串,且P的长度为奇数,那么整个区间的答案就应该是第一个P的奇数项前缀和+第二个P的偶数项前缀和+第一个P的奇数项的偏移量(奇数项和*P)

我们在处理P的时候,就计算2*P长度的前缀和,这样对于中间的段都是以2*P分段的

我们设[L,R]被分成[ml,len],n个2*P,1或0个P,[1,mr]这几段,ml,mr为段内下标

[ml,len]的部分很好算就是原来的值加上偏移量

然后[1,mr]部分就是原来的值

设k为中间P的段数

n个2*P的部分为原来n个2*P的前缀和+偏移量

偏移量是2*P的前缀和(奇数项还是偶数项要看[L,len]的奇偶)*{sigma((k-2*b)*len+mr) ,b属于[1,n]}

如果还有剩下的P,再加上P的值和偏移量


第三步:

额,没了

#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 100010
#define ll long long
#define mod 1000000007
using namespace std;
int t,ls;
char s[maxn];
int v[maxn<<1];
ll a[2][maxn<<1],b[2][maxn<<1];
void add(int t,int c,int d){
    for(int x=c;x<=2*ls;x+=x&(-x)){
        a[t][x]+=c*d,b[t][x]+=d;
        a[t][x]%=mod,b[t][x]%=mod;
    }
}
ll ask(ll *c,int x){
    ll ret=0;
    for(;x;x-=x&(-x)) ret+=c[x],ret%=mod;
    return ret;
}
void read(){
    scanf("%s",s+1);
    ls=strlen(s+1);
    for(int i=1;i<=ls;i++) v[i]=s[i]-'0';
    for(int i=ls+1;i<=2*ls;i++) v[i]=v[i-ls];
    memset(a,0,sizeof a);
    memset(b,0,sizeof b);
    for(int i=1;i<=2*ls;i++) add(i&1,i,v[i]);
}
ll calc(int t,ll ml,ll mr){
    ll ret=((mr+1)*(ask(b[t],mr)-ask(b[t],ml-1)))%mod-(ask(a[t],mr)-ask(a[t],ml-1))%mod;
    return ret%mod;
}
void solve(){
    int q,T,x,d;
    ll l,r;
    scanf("%d",&q);
    for(int i=0;i<q;i++){
        scanf("%d",&T);
        if(T==1){
            scanf("%d%d",&x,&d);
            add(x&1,x,-v[x]);
            add(x&1,x,d);
            add((x+ls)&1,x+ls,-v[x]);
            add((x+ls)&1,x+ls,d);
            v[x]=d;
        }else{
            scanf("%lld%lld",&l,&r);
            ll kl=(l+ls-1)/ls,kr=(r+ls-1)/ls;
            ll ml=l-(kl-1)*ls,mr=r-(kr-1)*ls;
            ll ret=0;
            ll kk=kr-kl-1,rr,sum;
            if(kl==kr){
                ret=calc(ml&1,ml,mr);
            }else{
                ret=calc(ml&1,ml,ls);
                ret=(ret+(ask(b[ml&1],ls)-ask(b[ml&1],ml-1))*(ls*(kk%mod)%mod+mr)%mod)%mod;
                sum=ask(b[1^((ls-ml+1)&1)],2*ls);
                ll bb=kk/2;
                if(bb>=1){
                    bb%=mod;
                    ret=(ret+calc(1^((ls-ml+1)&1),1,2*ls)*bb%mod)%mod;
                    ret=(ret+sum*( (mr+kk%mod*ls%mod)*bb%mod-bb*(bb+1)%mod*ls%mod )%mod)%mod;
                }
                if(kk&1){
                    ret=(ret+calc(1^((ls-ml+1)&1),1,ls))%mod;
                    ret=(ret+ask(b[1^((ls-ml+1)&1)],ls)*mr%mod)%mod;
                }
                rr=ls-ml+1+ls*kk;
                ret=(ret+calc(1^(rr&1),1,mr))%mod;
            }
            ret=(ret%mod+mod)%mod;
            printf("%lld\n",ret);
        }
    }
}
int main(){
    scanf("%d",&t);
    for(int ca=1;ca<=t;ca++){
        read();
        solve();
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值