【TJOI2018】洛谷4588 数学计算的解析(线段树)

题目:luogu4588.
题目大意:
对于一个数 x x x初始为 1 1 1,需要支持两种操作:
1.格式 1   m i 1\,m_i 1mi,使得 x = x ∗ m i x=x*m_i x=xmi,并输出 x % m o d x\%mod x%mod的值.
2.格式 2 p o s 2 pos 2pos,其中 p o s pos pos代表第 p o s pos pos次操作,使得 x = x m p o s x=\frac{x}{m_{pos}} x=mposx,并输出 x % m o d x\%mod x%mod的值.
操作次数 ≤ 1 0 5 \leq 10^5 105

不得不说这道题的思路确实挺好,但是想到也不是太难.

一开始我看到要对一个数取模还有除法,我第一个想到的方法就是直接特别暴力地使用逆元,然后直接做.

可是这道题由于模数不保证是质数,这代表这些数不一定有逆元,所以这道题其实并不能以一种数学角度去做.

考虑重新换一个思路,若我们不能把它们的乘积用一个数表示出来,而每一个除掉的数又在前面一定乘过,那么是不是可以维护每次操作的贡献呢?显然是可以的.

很容易发现,我们要维护操作序列上的两个操作,单点修改和查询前缀积,所以我们用树状数组或线段树直接实现就可以了,复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
const int INF=(1<<30)-1+(1<<30);
struct tree{
  int l,r;
  LL mul;
}tr[N*5];
int q;
LL mod;
void build(int L,int R,int k=1){
  tr[k].l=L;tr[k].r=R;
  if (L==R) return;
  int mid=L+R>>1;
  build(L,mid,k<<1),build(mid+1,R,k<<1|1);
}
void change(int x,LL num,int k=1){
  if (tr[k].l==tr[k].r){
    tr[k].mul=num%mod;
    return;
  }
  int mid=tr[k].l+tr[k].r>>1;
  if (x<=mid) change(x,num,k<<1);
  else change(x,num,k<<1|1);
  tr[k].mul=tr[k<<1].mul*tr[k<<1|1].mul%mod;
}
LL query(int L,int R,int k=1){
  if (L==tr[k].l&&R==tr[k].r) return tr[k].mul;
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return query(L,R,k<<1);
  else if (L>mid) return query(L,R,k<<1|1);
    else return query(L,mid,k<<1)*query(mid+1,R,k<<1|1)%mod;
}
Abigail into(){
  for (int i=0;i<N*5;i++)
    tr[i].l=tr[i].r=0,tr[i].mul=1;
  scanf("%d%lld",&q,&mod);
}
Abigail work(){
  int opt,pos;
  LL m;
  build(1,q);
  for (int i=1;i<=q;i++){
    scanf("%d",&opt);
    if (opt==1){
      scanf("%lld",&m);
      change(i,m);
      printf("%lld\n",query(1,i));
    }else{
      scanf("%d",&pos);
      change(pos,1);change(i,1);
      printf("%lld\n",query(1,i));
    }
  }
}
Abigail outo(){
}
int main(){
  int T;
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值