[TJOI2018] 数学计算
题目描述
小豆现在有一个数 x x x,初始值为 1 1 1。小豆有 Q Q Q 次操作,操作有两种类型:
1 m
:将
x
x
x 变为
x
×
m
x \times m
x×m,并输出
x
m
o
d
M
x \bmod M
xmodM
2 pos
:将
x
x
x 变为
x
x
x 除以第
p
o
s
pos
pos 次操作所乘的数(保证第
p
o
s
pos
pos 次操作一定为类型 1,对于每一个类型 1 的操作至多会被除一次),并输出
x
m
o
d
M
x \bmod M
xmodM。
输入格式
一共有 t t t 组输入。
对于每一组输入,第一行是两个数字 Q , M Q,M Q,M。
接下来 Q Q Q 行,每一行为操作类型 o p op op,操作编号或所乘的数字 m m m(保证所有的输入都是合法的)。
输出格式
对于每一个操作,输出一行,包含操作执行后的 x m o d M x \bmod M xmodM 的值。
样例 #1
样例输入 #1
1
10 1000000000
1 2
2 1
1 2
1 10
2 3
2 4
1 6
1 7
1 12
2 7
样例输出 #1
2
1
2
20
10
1
6
42
504
84
提示
对于 20 % 20\% 20% 的数据, 1 ≤ Q ≤ 500 1 \le Q \le 500 1≤Q≤500。
对于 100 % 100\% 100% 的数据, 1 ≤ Q ≤ 1 0 5 1 \le Q \le 10^5 1≤Q≤105, t ≤ 5 , M ≤ 1 0 9 t \le 5, M \le 10^9 t≤5,M≤109, 0 < m ≤ 1 0 9 0 < m \leq 10^9 0<m≤109。
这道题看着好像可以边计算边存储写出来,不过可以去交一下,嘿嘿嘿,红彤彤十分喜庆。
不绕弯子了,这道题可以用线段树来写(因为最近就在讲线段树 )
不过先不要头大,这就是最基础的线段树,点修点查就行了。
在第i次操作:
对于op为1的情况下,就将编号为i的点乘m
对于op为2的情况,就将编号为pos的点改为1,这样做只是改变了一次的操作,对于整体,是没有影响的。
最后,对每个点求乘积就能得到答案了。
每次查询的输出就是所有点乘积,f[1].ans
#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define int long long
using namespace std;
const int N=1e5+5;
struct node{
int l,r,ans;
}f[4*N];
int a[N];
int t;
int n,m;
void build(int p,int l,int r){//建树
f[p]={l,r,1};//因为是乘法,所以要初始化为1
if(l==r)return;
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void change(int p,int x,int k,int l){//点修改
if(f[p].l==x&&f[p].r==x){//找到点
//修改
f[p].ans*=k;
f[p].ans+=l;
return;
}
int mid=f[p].l+f[p].r>>1;
if(x<=mid)change(lc,x,k,l);
if(mid<x)change(rc,x,k,l);
f[p].ans=(f[lc].ans*f[rc].ans)%m;//求乘积
}
void out(){
printf("%lld\n",f[1].ans);
}
signed main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld",&n,&m);
build(1,1,n);
for(int i=1;i<=n;i++){
int op,k;
scanf("%lld%lld",&op,&k);
if(op==1){
change(1,i,k,0);//操作1,将第i个点乘上k
out();
}
else{
change(1,k,0,1);//操作2,将第k个点改为1
out();
}
}
}
}