[COGS2600] [FJWC2017] 交错和查询

题意

无限循环数字串 S 由长度为n的循环节 s 构成。设s 12345(n=5) ,则数字串 S 123451234512345
Si S 的第i位数字,在上面的例子中, S1=1,S2=2,S6=1
S 的一个子串S[l,r]的交错和为 sum(l,r)

sum(l,r)=SlSl+1+Sl+2Sl+3++(1)rlSr

sum(2,7)=23+45+12=3

现给定循环节 s ,要求支持两种操作:
1 pos digit
修改循环节 s 上的某一位,即将spos改为 digit
2 l r
S[l,r] 内所有子串的交错和的和

n,m200000;1lr1018;1posn;0digit9
答案对 109+7 的模。

题解

找规律,发现任意一个询问区间 [L,R] 对应的答案是,

SLLen+SL+2(Len2)+SL+4(len4)++SL+len0=i=0Len2SL+2i(Len2i)

那么构造四个数列, ji[x] 只保存原数列中奇数位置的数字, jiji[x] 保存原数列中奇数位置的数字乘它的位置, ou[x],ouou[x] 类似。
于是四个数列搞一搞,分情况讨论,最后求一些区间和。树状数组或线段树都可以维护。
下面是树状数组做法。

首先将 L,R 往前平移,使得 L [1,n]中。另外如果数列的长度为奇数,手动模拟时发现比较麻烦,这种情况可以将它加倍,视为一个偶数长度的序列,同时不能忘记修改时修改两个位置,数组大小变为2被。

  • 第一种情况 1LRn 此种情况比较简单。
  • 第二种情况 1Ln<R2n 此种情况将区间拆为 [L,n] [n+1,R] 考虑,也比较简单。
  • 第三种情况 1Ln<2n<kn<R(k+1)n 此种情况是最麻烦的,首先区间 [L,n],[kn,R] 可以用类似第二种的方法容易解决,而中间 [n,kn] 直接找规律即可。我们将一个循环一个循环罗列起来,每一行就是一个循环,现在就是求第 2,3,...,k 个循环的和。首先列出每个循环的式子,容易看出各个循环的和形成等差数列,可以快速求出。

上面三种情况解决这道题就解决了。
注意的是区间长度,循环个数可能很大,不小心很容易就爆long long。

代码

/// by ztx
/// blog.csdn.net/hzoi_ztx
#include <bits/stdc++.h>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rev(i,r,l) for(i=(r);i> (l);i--)
#define Each(i,v)  for(i=v.begin();i!=v.end();i++)
#define r(x)   read(x)
typedef long long ll ;
typedef double lf ;
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;
    if (CH == '-') NEG = true , CH = getchar() ;
    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;
    if (NEG) ret = -ret ;
}

#define kN  400010LL
#define mod 1000000007LL
#define half 500000004LL
#define lb  (p&(-p))

int n, a[kN], ji[kN], ou[kN], jiji[kN], ouou[kN];
bool doubled = false;

inline void Delta(int p,int w,int c[kN]) {
    for (;p<=n;p+=lb) (c[p] += w) %= mod;
}

inline int Sum(int p,int c[kN]) {
    int ret = 0;
    for (;p;p-=lb) (ret += c[p]) %= mod;
    return ret;
}

inline int Sum(int p,ll c1,int c[kN],int cc[kN]) {
    return (c1%mod * Sum(p,c)%mod + Sum(p,cc))%mod;
}

inline void Modify(int p,int w) {
    if (p&1)
        Delta(p,w-a[p],ji),
        Delta(p,(a[p]-w)*(p-1),jiji);
    else
        Delta(p,w-a[p],ou),
        Delta(p,(a[p]-w)*(p-2),ouou);
    a[p] = w;
}

inline int calc(int L,int R) {
    if (L&1)
        return (Sum(R,R-L+1+L-1,ji,jiji)-Sum(L-1,R-L+1+L-1,ji,jiji)) % mod;
    else
        return (Sum(R,R-L+1+L-2,ou,ouou)-Sum(L-1,R-L+1+L-2,ou,ouou)) % mod;
}

inline int calcL(int L,ll len) {
    if (L&1)
        return (Sum(n,len+L-1,ji,jiji)-Sum(L-1,len+L-1,ji,jiji)) % mod;
    else
        return (Sum(n,len+L-2,ou,ouou)-Sum(L-1,len+L-2,ou,ouou)) % mod;
}
inline int calcR(int L,ll R,ll len,ll dep) { 
    if (L&1) {
        len = (len+L-1-dep%mod*n%mod)%mod;
        return Sum(R-n*dep,len,ji,jiji);
    } else {
        len = (len+L-2-dep%mod*n%mod)%mod;
        return Sum(R-n*dep,len,ou,ouou);
    }
}
inline int calcM(int L,ll len,ll m) {
    ll A, B;
    len %= mod, m %= mod;
    if (L&1) {
        A = Sum(n,ji), B = Sum(n,jiji);
        return (B*m%mod + (len+L-1LL)%mod*A%mod*m%mod - A*n%mod*m%mod*(m+1)%mod*half%mod)%mod;
    } else {
        A = Sum(n,ou), B = Sum(n,ouou);
        return (B*m%mod + (len+L-2LL)%mod*A%mod*m%mod - A*n%mod*m%mod*(m+1)%mod*half%mod)%mod;
    }
}

inline void Query(ll L,ll R) {
    ll ans = 0LL;
    if (L > n)
        R -= n*((L-1LL)/n), L -= n*((L-1LL)/n);
    if (doubled && L>n/2)
        L -= n/2, R -= n/2;
    if (R <= n) ans = calc(L,R);
    else {
        ans = calcL(L,(R-L+1));
        if (R <= n*2) (ans += calcR(L,R,(R-L+1),1)) %= mod;
        else {
            (ans += calcR(L,R,(R-L+1),(R-1)/n)) %= mod;
            (ans += calcM(L,R-L+1,(R-1)/n-1)) %= mod;
        }
    }
    printf("%lld\n", (ans+mod)%mod);
}

int main() {
    freopen("summ.in","r",stdin);
    freopen("summ.out","w",stdout);
    int i, p, w, m;
    ll L, R;
    r(n);
    Rep (i,1,n) {
        while (CH=getchar(),CH<'!');
        a[i] = CH-'0';
    }
    if (n&1) {
        doubled = true;
        Rep (i,1,n) a[i+n] = a[i];
        n *= 2;
    }
    Rep (i,1,n) {
        if (i&1)
            Delta(i,a[i],ji),
            Delta(i,-a[i]*(i-1),jiji);
        else
            Delta(i,a[i],ou),
            Delta(i,-a[i]*(i-2),ouou);
    }
    r(m);
    while (m --> 0)
        if (r(i), i&1) {
            r(p), r(w);
            Modify(p,w);
            if (doubled) Modify(p+n/2,w);
        } else r(L), r(R), Query(L,R);
    END: getchar(), getchar();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值