注意到操作是可分裂、可合并的,所以一个很简单的想法是使用线段树维护每一个区间的答案。
虽然对于线段树上每个节点对应的操作序列中,区间的每一个位置的变换之间有可能有不同,但是至多只会存在区间长度种变换方式。我们可以在pushup的时候计算出这区间长度种变换的方式和这些方式对应的区间,这样每一次询问就只需要依次在当前查询区间对应的\(log\)个线段树的区间上二分出这个位置对应的变换方式就可以得到答案。
但是每一次都pushup显然太慢,不难注意到对于线段树上的区间\([l,r]\)只有当下标在\(l \sim r\)的所有操作都加入线段树之后,这个区间才有可能被询问到,所以我们使用二进制分组的思路,只在某一个区间的\(r\)位置插入操作的时候对这个区间pushup。这样我们可以得到复杂度为\(O(nlogn+qlog^2n)\)。
#include<bits/stdc++.h>
using namespace std;
int read(){
int a = 0; char c = getchar(); bool f = 0;
while(!isdigit(c)){f = c == '-'; c = getchar();}
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return f ? -a : a;
}
#define sz(x) (int)x.size()
const int _ = 1e5 + 7;
int M , N , arr[_] , lastans = 0 , cnt = 0;
struct node{int mn , a , b; friend bool operator <(node A , node B){return A.mn < B.mn;}};
namespace segt{
vector < node > nd[_ << 2];
#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)
void ins(int x , int l , int r , int tar , int l1 , int r1 , int a , int b){
if(l == r){
nd[x].push_back((node){0 , 1 , 0}); nd[x].push_back((node){l1 , a , b});
nd[x].push_back((node){r1 + 1 , 1 , 0}); nd[x].push_back((node){(int)1e9 , 0 , 0});
return;
}
mid >= tar ? ins(lch , l , mid , tar , l1 , r1 , a , b) : ins(rch , mid + 1 , r , tar , l1 , r1 , a , b);
if(r == tar){
vector < node > &l = nd[lch] , &r = nd[rch]; int pl = 0 , pr = 0;
while(pl + 1 < sz(l) || pr + 1 < sz(r)){
nd[x].push_back((node){max(l[pl].mn , r[pr].mn) , 1ll * l[pl].a * r[pr].a % M , (1ll * l[pl].b * r[pr].a + r[pr].b) % M});
if(l[pl + 1].mn == r[pr + 1].mn){++pl; ++pr;}
else l[pl + 1].mn < r[pr + 1].mn ? ++pl : ++pr;
}
nd[x].push_back((node){(int)1e9 , 0 , 0});
}
}
void qry(int x , int l , int r , int L , int R , int pos , int &val){
if(l >= L && r <= R){
auto t = upper_bound(nd[x].begin() , nd[x].end() , (node){pos , 0 , 0}) - 1;
val = (1ll * val * t->a + t->b) % M; return;
}
if(mid >= L) qry(lch , l , mid , L , R , pos , val);
if(mid < R) qry(rch , mid + 1 , r , L , R , pos , val);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
int id = read(); N = read(); M = read();
for(int i = 1 ; i <= N ; ++i) arr[i] = read();
for(int Q = read() ; Q ; --Q)
if(read() == 1){
int i = read() ^ ((id & 1) * lastans) , j = read() ^ ((id & 1) * lastans) , a = read() , b = read();
segt::ins(1 , 1 , 1e5 , ++cnt , i , j , a , b);
}
else{
int i = read() ^ ((id & 1) * lastans) , j = read() ^ ((id & 1) * lastans) , k = read() ^ ((id & 1) * lastans);
int val = arr[k]; segt::qry(1 , 1 , 1e5 , i , j , k , val); printf("%d\n" , lastans = val);
}
return 0;
}