HDU 5239 DOOM 线段树
标签(空格分隔): 线段树
题目链接:hdu5239
题意:给定长度为N的序列,有Q个操作,每次操作询问区间[l,r]的和,并将区间的值平方,每次输出所有之前查询答案的和对2^63-2^31取模。
思路:找规律发现任何数平方30次之后模mod都不再变化。于是用线段树维护区间内平方次数最少的元素为多少,弱最小的大于30则不进行操作,直接返回区间和,反之则继续递归直至叶子节点。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define Lson o<<1,l,mid
#define Rson o<<1|1,mid+1,r
const int maxn =100010;
int minv[maxn*4];
unsigned long long sum[maxn*4];
long long data[maxn];
const long long mod=((1ll<<63)-(1ll<<31));
void sqr(int i){
long long tp1,tp2;
tp1=tp2=sum[i];
__asm__("movq %1,%%rax\n imulq %2\n idivq %3\n":"=d"(sum[i]):"m"(tp1),"m"(tp2),"m"(mod):"%rax");
}
unsigned long long modify(int o,int l,int r,int L,int R){
if(minv[o]>31&&L<=l&&R>=r) return sum[o];
unsigned long long ret=0;
if(l==r){
ret=sum[o];
sqr(o);
minv[o]++;
return ret;
}
int mid=(l+r)>>1;
if(L<=mid)ret=(ret+modify(Lson,L,R))%mod;
if(R>mid) ret=(ret+modify(Rson,L,R))%mod;
sum[o]=(sum[o<<1]+sum[o<<1|1])%mod;
minv[o]=min(minv[o<<1],minv[o<<1|1]);
return ret;
}
void init(int o,int l,int r){
if(l==r){
minv[o]=0;
sum[o]=data[l];
return ;
}
int mid=(l+r)>>1;
init(Lson);init(Rson);
sum[o]=(sum[o<<1]+sum[o<<1|1])%mod;
minv[o]=0;
}
int main(){
int T,cas=0;
// freopen("data.in","r",stdin);
scanf("%d",&T);
while(T--){
memset(sum,0,sizeof(sum));
printf("Case #%d:\n",++cas);
int n,q,l,r;
unsigned long long ans=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%I64d",data+i);
init(1,1,n);
while(q--){
scanf("%d%d",&l,&r);
ans=(ans+modify(1,1,n,l,r))%mod;
printf("%I64d\n",(long long)ans);
}
}
return 0;
}