HDU 5239 上海大都会 D题(线段树+数论)

打表,发现规律是存在一定次数(较小)后,会出现a=(a*a)%p。可以明显地发现本题与线段树有关。设置标记flag,记录本段内的数是否均已a=a*a%p。若是,则不需更新,否则更新有叶子结点,再pushup。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL unsigned long long 
using namespace std;
const LL p=9223372034707292160uLL;
const int N=100050;
int n;
LL seg[N<<2],s;
bool flag[N<<2];

void build(int rt,int l,int r){
	flag[rt]=false;
	if(l==r){
		scanf("%llu",&seg[rt]);
	//	cout<<l<<"="<<seg[rt]<<endl;
		return ;
	}
	int m=(l+r)>>1;
	build(rt<<1,l,m);
	build(rt<<1|1,m+1,r);
	seg[rt]=(seg[rt<<1]+seg[rt<<1|1])%p;
}

LL mul(LL a,LL b){
	LL res=0;
	while(b){
		if(b&1) res=(res+a)%p;
		b>>=1;
		a=(a+a)%p;
	}
	return res;
}

void update(int rt,int l,int r,int L,int R){
	if(flag[rt]&&l<=L&&R<=r){
		s=(s+seg[rt])%p;
		return ;
	}
	if(L==R){
		s=(s+seg[rt])%p;
		LL tmp=mul(seg[rt],seg[rt]);
		if(seg[rt]==tmp){
			flag[rt]=true;
		}
		seg[rt]=tmp;
		return ;
	}
	int m=(L+R)>>1;
	if(r<=m){
		update(rt<<1,l,r,L,m);
	}
	else if(l>=m+1) update(rt<<1|1,l,r,m+1,R);
	else{
		update(rt<<1,l,r,L,m);
		update(rt<<1|1,l,r,m+1,R);
	}
	flag[rt]=flag[rt<<1]&flag[rt<<1|1];
	seg[rt]=(seg[rt<<1]+seg[rt<<1|1])%p;
}

int main(){
	int T,icase=0,k,l,r;
	scanf("%d",&T);
	while(T--){
		s=0;
		scanf("%d%d",&n,&k);
		build(1,1,n);
		printf("Case #%d:
",++icase);
		for(int i=1;i<=k;i++){
			scanf("%d%d",&l,&r);
			update(1,l,r,1,n);
			printf("%llu
",s);
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/jie-dcai/p/4539908.html