P4588 [TJOI2018] 数学计算

[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 1Q500

对于 100 % 100\% 100% 的数据, 1 ≤ Q ≤ 1 0 5 1 \le Q \le 10^5 1Q105 t ≤ 5 , M ≤ 1 0 9 t \le 5, M \le 10^9 t5,M109 0 < m ≤ 1 0 9 0 < m \leq 10^9 0<m109

这道题看着好像可以边计算边存储写出来,不过可以去交一下,嘿嘿嘿,红彤彤十分喜庆。

不绕弯子了,这道题可以用线段树来写(因为最近就在讲线段树
不过先不要头大,这就是最基础的线段树,点修点查就行了。
在第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();
			}
		}
	}
}
  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值