[TJOI2018] 数学计算
题目描述
小豆现在有一个数 xxx,初始值为 111。小豆有 QQQ 次操作,操作有两种类型:
1 m
:将 xxx 变为 x×mx \times mx×m,并输出 x mod Mx \bmod MxmodM
2 pos
:将 xxx 变为 xxx 除以第 pospospos 次操作所乘的数(保证第 pospospos 次操作一定为类型 1,对于每一个类型 1 的操作至多会被除一次),并输出 x mod Mx \bmod MxmodM。
输入格式
一共有 ttt 组输入。
对于每一组输入,第一行是两个数字 Q,MQ,MQ,M。
接下来 QQQ 行,每一行为操作类型 opopop,操作编号或所乘的数字 mmm(保证所有的输入都是合法的)。
输出格式
对于每一个操作,输出一行,包含操作执行后的 x mod Mx \bmod MxmodM 的值。
样例 #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≤5001 \le Q \le 5001≤Q≤500。
对于 100%100\%100% 的数据,1≤Q≤1051 \le Q \le 10^51≤Q≤105,t≤5,M≤109t \le 5, M \le 10^9t≤5,M≤109,0<m≤1090 < m \leq 10^90<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();
}
}
}
}