题意:给一个0~9组成的串,有两种操作,1是改变某个位置pos的值,2是计算某一段l~r的
G(l, r) = Sl - Sl+1 + Sl+2 - ... + (-1)r-lSr
不过,这个串是无限周期循环的,输入给的是一个循环节内的内容,而且l和r的范围会很大。
思路:线段树。首先我们需要推出一个公式,比如对于串abcde,G(1,5) =5*a+3*c+1*e,用语言描述就是实际上只有从左到右的奇数位参与求和,第一个数的系数是区间长度,往后逐个递减2。所以,线段树需要维护4个内容,区间内的奇数位和,偶数位和,G(区间左边,区间右边),G(区间左边+1,区间右边),具体维护方法见代码。这样就可以计算循环节内的G了。
但是循环节外的怎么办,其实我们可以把它分为三段,中间那一段是完成的若干个循环节,最左边和最右边是另外两段,这两段可能是不完整的。然后分别计算三段再合并就可以了。其中,中间那一段可能跨过了非常多个循环节,不能逐个合并,这里有用到了快速幂的思想,具体见代码,三段合并后,就是长区间的结果。
这题坑了我好久,主要是两个问题,大范围没有去去模转换到循环节内。还有就是爆long long的问题。下面上代码。。。
#include<iostream>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
#include<string.h>
#include<cstdio>
using namespace std;
#define maxn 100010
#define ll long long
#define mod 1000000007ull
char P[maxn];
ll len;
struct node{
ll l,r;
ll sum1; //奇数位和
ll sum0; //偶数位和
ll val1; //奇数位阶梯和
ll val0; //偶数位阶梯和
};
node tree[maxn*4];
void push_up(node& p,node& lch,node& rch){
ll d=rch.r-rch.l;
rch.l=lch.r+1;
rch.r=rch.l+d;
p.l=lch.l; p.r=rch.r;
ll llen=lch.r-lch.l+1;
ll rlen=rch.r-rch.l+1;
p.val1=lch.val1+lch.sum1*(rlen%mod);
p.val0=lch.val0+lch.sum0*(rlen%mod);
p.sum1=lch.sum1;
p.sum0=lch.sum0;
if(llen&1){
p.val1+=rch.val0;
p.val0+=rch.val1;
p.sum1+=rch.sum0;
p.sum0+=rch.sum1;
}else{
p.val1+=rch.val1;
p.val0+=rch.val0;
p.sum1+=rch.sum1;
p.sum0+=rch.sum0;
}
p.val1%=mod; p.val0%=mod; p.sum1%=mod; p.sum0%=mod;
}
void build_tree(ll n,ll l,ll r){
tree[n].l=l; tree[n].r=r;
if(l==r){
tree[n].val1=tree[n].sum1=P[l]-'0';
tree[n].val0=tree[n].sum0=0;
return;
}
ll mid=(l+r)/2;
build_tree(n<<1,l,mid);
build_tree((n<<1)|1,mid+1,r);
push_up(tree[n],tree[n<<1],tree[(n<<1)|1]);
}
node query(ll n,ll l,ll r){
if(tree[n].l==l&&tree[n].r==r){
return tree[n];
}
ll mid=(tree[n].l+tree[n].r)/2;
if(r<=mid){
return query(n<<1,l,r);
}else{
if(l>mid){
return query((n<<1)|1,l,r);
}else{
node re;
node lch=query(n<<1,l,mid);
node rch=query((n<<1)|1,mid+1,r);
push_up(re,lch,rch);
return re;
}
}
}
void update(ll n,ll pos,ll val){
if(tree[n].l==tree[n].r){
tree[n].val1=tree[n].sum1=val;
tree[n].val0=tree[n].sum0=0;
return;
}
ll mid=(tree[n].l+tree[n].r)/2;
if(pos<=mid){
update(n<<1,pos,val);
}else{
update((n<<1)|1,pos,val);
}
push_up(tree[n],tree[n<<1],tree[(n<<1)|1]);
}
//长区间快速合并
node mul(ll n){
node one=tree[1];
node re;
bool flag=1;
while(n){
if(n&1){
if(flag){
re=one;
flag=0;
}else{
node tmp;
push_up(tmp,one,re);
re=tmp;
}
}
node tmp;
node oneL=one;
node oneR=one;
push_up(tmp,oneL,oneR);
one=tmp;
n>>=1;
}
return re;
}
int main(){
int t;
cin>>t;
while(t--){
scanf("%s",P+1);
len=strlen(P+1);
build_tree(1,1,len);
ll q;
cin>>q;
for(int i=1;i<=q;i++){
ll op;
scanf("%lld",&op);
if(op==1){ //update
ll a,b;
scanf("%lld%lld",&a,&b);
update(1,a,b);
}else{ //query
ll a,b;
scanf("%lld%lld",&a,&b);
ll cnt=(b-1)/len-(a-1)/len;
if(cnt>1){
node M=mul(cnt-1);
ll ls=a%len; if(!ls)ls=len;
ll rt=b%len; if(!rt)rt=len;
node lch=query(1,ls,len);
node rch=query(1,1,rt);
node tmp;
push_up(tmp,lch,M);
node ans;
push_up(ans,tmp,rch);
printf("%lld\n",ans.val1%mod);
}else if(cnt==1){
ll ls=a%len; if(!ls)ls=len;
ll rt=b%len; if(!rt)rt=len;
node lch=query(1,ls,len);
node rch=query(1,1,rt);
node ans;
push_up(ans,lch,rch);
printf("%lld\n",ans.val1%mod);
}else{
a%=len; if(!a)a=len;
b%=len; if(!b)b=len;
node ans=query(1,a,b);
printf("%lld\n",ans.val1%mod);
}
}
}
}
return 0;
}
本文介绍如何使用线段树解决给定无限周期循环串上的特定操作问题,包括改变位置元素和计算区间G函数。通过快速幂思想解决循环节外的计算,并详细解释了线段树的维护方法。
283

被折叠的 条评论
为什么被折叠?



