打表,发现规律是存在一定次数(较小)后,会出现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; }