题意:
有一个长度很大(10^18)的序列是由长度为n的序列不断循环构成的。长度为n的序列给定且数值均为0~9。每次有两个操作。
1 修改循环节上的一位。
2 询问 $[l,r]$ 内所有连续子串的交错和的和,$1<=l,r<=10^{18}$。
一个子串$[l,r]$的交错和$=a[l]-a[l+1]+a[l+2]-...+(-1)^{r-l}a[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; }