洛谷P4588 [TJOI2018]数学计算 题解
题目链接:P4588 [TJOI2018]数学计算
题意:
cxy 现在有一个数 $x$,初始值为 $1$。cxy 有 $Q$ 次操作,操作有两种类型:
1 m
:将 $x$ 变为 $x \times m$,并输出 $x \bmod M$ 。
2 pos
:将 $x$ 变为 $x$ 除以第 $\mathtt{pos}$ 次操作所乘的数(保证第 $\mathtt{pos}$ 次操作一定为类型 $1$,对于每一个类型 $1$ 的操作至多会被除一次),并输出 $x \bmod M$。输入格式:
一共有 $t$ 组输入。
对于每一组输入,第一行是两个数字 $Q,M$。
接下来 $Q$ 行,每一行为操作类型 $\mathtt{op}$,操作编号或所乘的数字 $m$(保证所有的输入都是合法的)。
输出格式:
对于每一个操作,输出一行,包含操作执行后的 $x \bmod M$ 的值。
数据范围:
对于 $100\%$ 的数据,$1 \le Q \le 10^5$,$t \le 5, M \le 10^9$,$0 < m \leq 10^9$。
直接模拟是不行的。因为如果你要记录乘积,中间模过以后就不好搞除法了
逆元?不行,因为 $M$ 没有保证是质数,所以可能不存在逆元。
如果记录每个数,那每次直接乘过去就是暴力了,显然不可行。
考虑优化暴力。众所周知 $x \times 1 = x$
因此我们还是可以维护这样的序列,对于每个除法操作,我们只要把对应位置的数改成 $1$ 就好了
那这就是一个单点修改+区间查询乘积,线段树维护(我感觉树状数组也可以)
时间复杂度 $\mathcal{O}(n \log n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5+15))
#define ls(at) (at << 1)
#define rs(at) (at << 1 | 1)
int n,mod,ans[N * 4];
void push_up(int at)
{ ans[at] = ans[ls(at)] * ans[rs(at)] % mod; }
void build(int l,int r,int at)
{
if(l == r) { return ans[at] = 1, void(0); }
int mid = (l+r) >> 1;
build(l,mid,ls(at)); build(mid+1,r,rs(at));
push_up(at);
}
void modify(int x,int k,int l=1,int r=n,int at=1)
{
if(l == r) { return ans[at] = k, void(0); }
int mid = (l+r) >> 1;
if(x <= mid) modify(x,k,l,mid,ls(at));
else modify(x,k,mid+1,r,rs(at));
push_up(at);
}
void solve()
{
cin >> n >> mod; build(1,n,1);
for(int i=1,op,p; i<=n; i++)
{
cin >> op >> p;
if(op & 1) { modify(i,p); }
else { modify(p,1); }
cout << ans[1] << '\n';
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int Q; cin >> Q; while(Q--) solve();
return 0;
}