P4588 [TJOI2018]数学计算 题解

题目传送门

题意简述

x x x 初值为 1 1 1,要求支持以下操作:

操作 1:将 x ← x ⋅ m x\leftarrow x\cdot m xxm,输出 x m o d    M x \mod M xmodM

操作 2:将 x ← x ÷ k x\leftarrow x \div k xx÷k k k k 是第 m m m 次操作时乘上的数,输出 x m o d    M x\mod M xmodM

分析

线段树好题!线段树好题!线段树好题!

做过这道题后,我真的对线段树有了更加深厚的了解。

看到这个题目其实很难想象可以使用线段树来做,但我们可以发现,如果我们按照时间轴,即 1 ∼ Q 1\sim Q 1Q 建一棵线段树,第 1 1 1 个数即为 x x x,一开始全部赋初值 1 1 1

i i i 个操作为 1 时就单点修改 i ← m i\leftarrow m im,这样 push_up时就可以将 x ← x ⋅ m x\leftarrow x \cdot m xxm

为 2 时可以单点修改 i ← 1 i\leftarrow 1 i1,这样 push_up时, x x x 就相当于除以了 k k k(第 m m m 次操作乘上的数)。

每次查询,输出 tr[1].sum即可。

这样一分析,就变成了线段树裸题了(甚至还简单,都不用区间查询)。

代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;

inline int read(){
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f^=!(ch^45),ch=getchar();
	while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline void write(int x){
	if(x<0)x=-x,putchar('-');
	if(x>=10)write(x/10);
	putchar(x%10+'0');
}
inline void writeln(int x){write(x);puts("");}

int T,Q,mod;

struct segment{
	int l,r,sum;
}tr[100005<<2];
inline int ls(int p){return p<<1;}
inline int rs(int p){return p<<1|1;}
inline void push_up(int p){tr[p].sum=(tr[ls(p)].sum*tr[rs(p)].sum)%mod;}
void build(int p,int l,int r){
	tr[p]={l,r,1};
	if(l==r)return;
	int mid=l+r>>1;
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
	push_up(p);
}

void update(int p,int x,int k){
	if(tr[p].l==tr[p].r){
		tr[p].sum=k;
		return;
	}
	int mid=tr[p].l+tr[p].r>>1;
	if(x<=mid)update(ls(p),x,k);
	if(mid<x)update(rs(p),x,k);
	push_up(p);
}

signed main(){
	T=read();
	while(T--){
		Q=read();mod=read();
		memset(tr,0,sizeof tr);
		build(1,1,Q);
		for(int i=1;i<=Q;i++){
			int p=read(),m=read();
			if(p==1)update(1,i,m),writeln(tr[1].sum%mod);
			else update(1,m,1),writeln(tr[1].sum%mod);
		}
	}
	#ifndef ONLINE_JUDGE
	system("pause");
	#endif
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值