题目:luogu4588.
题目大意:
对于一个数
x
x
x初始为
1
1
1,需要支持两种操作:
1.格式
1
 
m
i
1\,m_i
1mi,使得
x
=
x
∗
m
i
x=x*m_i
x=x∗mi,并输出
x
%
m
o
d
x\%mod
x%mod的值.
2.格式
2
p
o
s
2 pos
2pos,其中
p
o
s
pos
pos代表第
p
o
s
pos
pos次操作,使得
x
=
x
m
p
o
s
x=\frac{x}{m_{pos}}
x=mposx,并输出
x
%
m
o
d
x\%mod
x%mod的值.
操作次数
≤
1
0
5
\leq 10^5
≤105
不得不说这道题的思路确实挺好,但是想到也不是太难.
一开始我看到要对一个数取模还有除法,我第一个想到的方法就是直接特别暴力地使用逆元,然后直接做.
可是这道题由于模数不保证是质数,这代表这些数不一定有逆元,所以这道题其实并不能以一种数学角度去做.
考虑重新换一个思路,若我们不能把它们的乘积用一个数表示出来,而每一个除掉的数又在前面一定乘过,那么是不是可以维护每次操作的贡献呢?显然是可以的.
很容易发现,我们要维护操作序列上的两个操作,单点修改和查询前缀积,所以我们用树状数组或线段树直接实现就可以了,复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
const int INF=(1<<30)-1+(1<<30);
struct tree{
int l,r;
LL mul;
}tr[N*5];
int q;
LL mod;
void build(int L,int R,int k=1){
tr[k].l=L;tr[k].r=R;
if (L==R) return;
int mid=L+R>>1;
build(L,mid,k<<1),build(mid+1,R,k<<1|1);
}
void change(int x,LL num,int k=1){
if (tr[k].l==tr[k].r){
tr[k].mul=num%mod;
return;
}
int mid=tr[k].l+tr[k].r>>1;
if (x<=mid) change(x,num,k<<1);
else change(x,num,k<<1|1);
tr[k].mul=tr[k<<1].mul*tr[k<<1|1].mul%mod;
}
LL query(int L,int R,int k=1){
if (L==tr[k].l&&R==tr[k].r) return tr[k].mul;
int mid=tr[k].l+tr[k].r>>1;
if (R<=mid) return query(L,R,k<<1);
else if (L>mid) return query(L,R,k<<1|1);
else return query(L,mid,k<<1)*query(mid+1,R,k<<1|1)%mod;
}
Abigail into(){
for (int i=0;i<N*5;i++)
tr[i].l=tr[i].r=0,tr[i].mul=1;
scanf("%d%lld",&q,&mod);
}
Abigail work(){
int opt,pos;
LL m;
build(1,q);
for (int i=1;i<=q;i++){
scanf("%d",&opt);
if (opt==1){
scanf("%lld",&m);
change(i,m);
printf("%lld\n",query(1,i));
}else{
scanf("%d",&pos);
change(pos,1);change(i,1);
printf("%lld\n",query(1,i));
}
}
}
Abigail outo(){
}
int main(){
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}