题意:
维护一个数列,数列有两种操作:
1.将区间[L,R]中的每个数都变成3次方,即a[i] --> a[i]*a[i]*a[i]
2.询问区间[L,R]的和(取模99971)
思路:
打表可以发现所有数字在取模的情况下循环节的最小公倍数为48,故维护一个48倍内容的线段树。
更新可以在对应区间节点处打上更新标记,向上维护时可以将其看出更新偏转,向下询问定义一个变量保存更新次数,最后输出。
总结:
写题目应该养成二维数组数字较大维在前的习惯
#include<bits/stdc++.h>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
typedef long long LL;
const int maxn = 100010;
const int mod = 99971;
int sum[maxn<<2][50],cnt[maxn<<2];
void push_up( int rt )
{
for ( int i=0 ; i<48 ; i++ )
sum[rt][i] = (sum[lson][(i+cnt[lson])%48]+sum[rson][(i+cnt[rson])%48])%mod;
}
void build( int l , int r , int rt )
{
cnt[rt] = 0;
if ( l==r )
{
scanf ( "%d" , &sum[rt][0] );
sum[rt][0] %= mod;
for ( int i=1 ; i<48 ; i++ )
sum[rt][i] = 1LL*sum[rt][i-1]*sum[rt][i-1]*sum[rt][i-1]%mod;
return;
}
int mid = ( l+r )>>1;
build ( l , mid , lson );
build ( mid+1 , r , rson );
push_up( rt );
}
void update( int L , int R , int l , int r , int rt )
{
if ( L<=l&&R>=r )
{
cnt[rt]++;
return;
}
int mid = ( l+r )>>1;
if ( L<=mid ) update( L , R , l , mid , lson );
if ( R> mid ) update( L , R , mid+1 , r , rson );
push_up ( rt );
}
int query( int L , int R , int l , int r , int rt , int x )
{
x = ( x+cnt[rt] )%48;
if ( L<=l&&R>=r ) return sum[rt][x];
int mid = ( l+r )>>1,res = 0;
if ( L<=mid ) res += query( L , R , l , mid , lson , x );
if ( R> mid ) res += query( L , R , mid+1 , r , rson , x );
return res%mod;
}
int main()
{
int T; scanf ( "%d" , &T );
for ( int cas=1 ; cas<=T ; cas++ )
{
int n,m; scanf ( "%d%d" , &n , &m );
build( 1 , n , 1 );
for ( int i=1 ; i<=m ; i++ )
{
int op,l,r; scanf( "%d%d%d" , &op , &l , &r );
if ( op==1 ) update( l , r , 1 , n , 1 );
else printf ( "%d\n" , query( l , r , 1 , n , 1 , 0 ) );
}
}
return 0;
}