1. 程式人生 > >2018 ccpc女生賽 口算訓練 (二分 + 唯一分解定理)

2018 ccpc女生賽 口算訓練 (二分 + 唯一分解定理)

小Q非常喜歡數學,但是他的口算能力非常弱。因此他找到了小T,給了小T一個長度為nn的正整數序列a1,a2,...,ana1,a2,...,an,要求小T丟擲mm個問題以訓練他的口算能力。 

每個問題給出三個正整數l,r,dl,r,d,小Q需要通過口算快速判斷al×al+1×...×ar1×aral×al+1×...×ar−1×ar是不是dd的倍數。 

小Q迅速地回答了出來,但是小T並不知道正確答案是什麼,請寫一個程式幫助小T計算這些問題的正確答案。Input第一行包含一個正整數T(1T10)T(1≤T≤10),表示測試資料的組數。 

每組資料第一行包含兩個正整數n,m(1n,m100000)n,m(1≤n,m≤100000),分別表示序列長度以及問題個數。 

第二行包含nn個正整數a1,a2
,...,an(1ai100000)
a1,a2,...,an(1≤ai≤100000)
,表示序列中的每個數。 

接下來mm行,每行三個正整數l,r,d(1lrn,1d100000)l,r,d(1≤l≤r≤n,1≤d≤100000),表示每個問題。Output對於每個問題輸出一行,若是倍數,輸出Yes,否則輸出No。Sample Input
1
5 4
6 4 7 2 5
1 2 24
1 3 18
2 5 17
3 5 35
Sample Output
Yes
No
No
Yes

先用vector記錄下每個因子出現的位置。vector裡存的是含有i這個因子的下標。

查詢l到r裡因子個數時用upper_bound() - lower_bound()就可以。 

#include<bits/stdc++.h>
using namespace std;
vector<int> a[100005];
int T, n, m;
void init(int x, int Index){
	for(int i = 2; i * i <= x; i++){
		if(x % i == 0){
			while(x % i == 0){
				x /= i;
				a[i].push_back(Index);
			}
		}
	}
	if(x > 1)
		a[x].push_back(Index);
}

int query(int l, int r, int x){
	return upper_bound(a[x].begin(), a[x].end(), r) - lower_bound(a[x].begin(), a[x].end(), l);
}

int judge(int l, int r, int x){
	for(int i = 2; i * i <= x; i++){
		if(x % i == 0){
			int t = 0;
			while(x % i == 0){
				x /= i;
				t++;
			}
			int c = query(l, r, i);
			if(t > c)
				return 0;
		}
	}
	if(x > 1 && query(l, r, x) < 1)
		return 0;
	return 1;
}

int main()
{
	scanf("%d", &T);
	while(T--){
		for(int i = 0; i < 100005; i++)
			a[i].clear();
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++){
			int x;
			scanf("%d", &x);
			init(x, i);
		}
		for(int i = 0; i < m; i++){
			int l, r, d;
			scanf("%d%d%d", &l, &r, &d);
			int flag = judge(l, r, d);
			if(flag)
				printf("Yes\n");
			else
				printf("No\n");
		}
	}
	return 0;
}