P4588 [TJOI2018]数学计算(线段树维护区间乘和单点修改)

P4588 [TJOI2018]数学计算

刚看到这题根本每想到用线段树,直接每次记录计算结果然后找到要除的数字就好了呗
但是!你会注意到,如果连续乘很多很多次,然后再除的话,如果不取模会爆 long long 取模了,做除法结果就不对了,所以想到用线段树,以时间为轴建树,维护每个叶子节点为对应操作时间的乘数,做除法时将对应除数修改为 1 再查询区间乘即可。

#include<iostream>
#include<cstdio>
#define ll long long
#define lson p<<1
#define rson p<<1|1
#define mid s+((t-s)>>1)

using namespace std;
const int maxn = 2e5+10;
ll tr[maxn<<2];
ll mod;

inline void up(int p) {
	tr[p] = (tr[p*2]*tr[p*2+1])%mod;
} 

void build (int p, int s, int t) {
	if(s == t) {
		tr[p] = 1;
		return ;
	}
	build(lson, s, mid); build(rson, mid+1, t);
	up(p);
}

void change(int l, int r, int s, int t, int k, int p) { // s, t 为大的二分区间 
	if(l <= s && r >= t) { // 修改区间 l = r (已经经过离散化了,将点离散成区间) 
		tr[p] = k; // 单点修改 
		return ;
	}
	if(l <= mid) change(l, r, s, mid, k, lson);
	if(r > mid) change(l, r, mid+1, t, k, rson);
	up(p);
}

int main() {
//	freopen("test.in", "r", stdin);
	int T;
	scanf("%d", &T);
	while(T--) {
		int q, op, m;
		scanf("%d%lld", &q, &mod);
		build(1, 1, q); // 虽然除法的叶节点一定是1, 但是不确定哪些是除法,确保树叶够多 
		for(int i = 1; i <= q; i++) {
			scanf("%d%d", &op, &m);
			if(op == 1) {;
				change(i, i, 1, q, m, 1);
				tr[1] %= mod; // 最终的乘法结果维护在根节点 
				
			}
			else change(m, m, 1, q, 1, 1); // 找到 m 对应的叶节点,修改为 1
			printf("%lld\n", tr[1]%mod); 
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值