數學計算(線段樹乘法)

數學計算(線段樹乘法)

Describe

小豆如今有一個數 x,初始值爲 1 。 小豆有 Q次操做,操做有兩種類型:c++

  • 1 m: x=x×m,輸出 xmodM;
  • 2 pos: x=x/ 第 pos 次操做所乘的數(保證第 pos 次操做必定爲類型 1,對於每個類型 1 的操做至多會被除一次),輸出 xmodM。

Input

一共有 t 組輸入。
對於每一組輸入,第一行是兩個數字 Q,M 。
接下來 Q 行,每一行爲操做類型 op ,操做編號或所乘的數字 m (保證全部的輸入都是合法的)。數組

Output

對於每個操做,輸出一行,包含操做執行後的 xmodM 的值spa

樣例輸入

1
10 1000000000
1 2
2 1
1 2
1 10
2 3
2 4
1 6
1 7
1 12
2 7

樣例輸入

2
1
2
20
10
1
6
42
504
84

Hint

對於 20% 的數據, 1≤Q≤500;
對於 100% 的數據, 1≤Q≤\(10^5\),t≤5,M≤\(10^9\).code

Solution

​ 1. 第一種暴力,直接乘除取模,錯誤,由於每次取模後獲得的數,不必定能被要求的數整除,一旦不能整除就全都是0了。input

​ 2. 第二種是求逆元,但模數與原數不必定互質,捨去。數學

​ 3.因此咱們能夠選另外一種線段樹的方法,定義一個全是1的數組,將每一個1操做要乘的數做爲數組中一個值,加線段樹維護積(樹的根),若是是2操做,就把數組中的對應的數(也就是積的一個因子)改成1便可,改一下樹中涉及到的每一個節點。string

​ 樹的節點等於左右兒子乘積取模。所以這是一道單點修改線段樹的題,連建樹都不用,樹節點直接初始爲1便可。it

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll tr[maxn<<2],mod,n;
void modi(ll rt,ll l,ll r,ll x,ll y){
	if(l==r){
		tr[rt]=y;
		return;
	}
	ll mid=(l+r)>>1;
	if(x<=mid)modi(rt<<1,l,mid,x,y);
	else modi(rt<<1|1,mid+1,r,x,y);
	tr[rt]=(tr[rt<<1]%mod)*(tr[rt<<1|1]%mod)%mod;
}
int main(){
	int t;scanf("%d",&t);
	while(t--){
		for(int i=1;i<=4e5+5;++i)tr[i]=1;
		scanf("%lld%lld",&n,&mod);
		ll x,y;
		for(ll i=1LL;i<=n;++i){
			scanf("%lld%lld",&x,&y);
			if(x==1)modi(1,1,n,i,y);
			else modi(1,1,n,y,1);
			printf("%lld\n",tr[1]);
		}
	}
	return 0;
}
相關文章
相關標籤/搜索