zoj 3813 Alternating Sum

        题意:给一个0~9组成的串,有两种操作,1是改变某个位置pos的值,2是计算某一段l~r的

G(lr) = 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;
}



你可能感兴趣的:(线段树)