小豆如今有一个数 x,初始值为 1 。 小豆有 Q次操做,操做有两种类型:c++
1 m
: x=x×m,输出 xmodM;2 pos
: x=x/ 第 pos 次操做所乘的数(保证第 pos 次操做必定为类型 1,对于每个类型 1 的操做至多会被除一次),输出 xmodM。一共有 t 组输入。
对于每一组输入,第一行是两个数字 Q,M 。
接下来 Q 行,每一行为操做类型 op ,操做编号或所乘的数字 m (保证全部的输入都是合法的)。数组
对于每个操做,输出一行,包含操做执行后的 xmodM 的值spa
1 10 1000000000 1 2 2 1 1 2 1 10 2 3 2 4 1 6 1 7 1 12 2 7
2 1 2 20 10 1 6 42 504 84
对于 20% 的数据, 1≤Q≤500;
对于 100% 的数据, 1≤Q≤\(10^5\),t≤5,M≤\(10^9\).code
1. 第一种暴力,直接乘除取模,错误,由于每次取模后获得的数,不必定能被要求的数整除,一旦不能整除就全都是0了。input
2. 第二种是求逆元,但模数与原数不必定互质,舍去。数学
3.因此咱们能够选另外一种线段树的方法,定义一个全是1的数组,将每一个1操做要乘的数做为数组中一个值,加线段树维护积(树的根),若是是2操做,就把数组中的对应的数(也就是积的一个因子)改成1便可,改一下树中涉及到的每一个节点。string
树的节点等于左右儿子乘积取模。所以这是一道单点修改线段树的题,连建树都不用,树节点直接初始为1便可。it
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long ll; const int maxn=1e5+5; ll tr[maxn<<2],mod,n; void modi(ll rt,ll l,ll r,ll x,ll y){ if(l==r){ tr[rt]=y; return; } ll mid=(l+r)>>1; if(x<=mid)modi(rt<<1,l,mid,x,y); else modi(rt<<1|1,mid+1,r,x,y); tr[rt]=(tr[rt<<1]%mod)*(tr[rt<<1|1]%mod)%mod; } int main(){ int t;scanf("%d",&t); while(t--){ for(int i=1;i<=4e5+5;++i)tr[i]=1; scanf("%lld%lld",&n,&mod); ll x,y; for(ll i=1LL;i<=n;++i){ scanf("%lld%lld",&x,&y); if(x==1)modi(1,1,n,i,y); else modi(1,1,n,y,1); printf("%lld\n",tr[1]); } } return 0; }