口算训练
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 512000/512000 K (Java/Others)Total Submission(s): 1722 Accepted Submission(s): 368
Problem Description
小Q非常喜欢数学,但是他的口算能力非常弱。因此他找到了小T,给了小T一个长度为
n的正整数序列
a1,a2,...,an,要求小T抛出
m个问题以训练他的口算能力。
每个问题给出三个正整数 l,r,d,小Q需要通过口算快速判断 al×al+1×...×ar−1×ar是不是 d的倍数。
小Q迅速地回答了出来,但是小T并不知道正确答案是什么,请写一个程序帮助小T计算这些问题的正确答案。
每个问题给出三个正整数 l,r,d,小Q需要通过口算快速判断 al×al+1×...×ar−1×ar是不是 d的倍数。
小Q迅速地回答了出来,但是小T并不知道正确答案是什么,请写一个程序帮助小T计算这些问题的正确答案。
Input
第一行包含一个正整数
T(1≤T≤10),表示测试数据的组数。
每组数据第一行包含两个正整数 n,m(1≤n,m≤100000),分别表示序列长度以及问题个数。
第二行包含 n个正整数 a1,a2,...,an(1≤ai≤100000),表示序列中的每个数。
接下来 m行,每行三个正整数 l,r,d(1≤l≤r≤n,1≤d≤100000),表示每个问题。
每组数据第一行包含两个正整数 n,m(1≤n,m≤100000),分别表示序列长度以及问题个数。
第二行包含 n个正整数 a1,a2,...,an(1≤ai≤100000),表示序列中的每个数。
接下来 m行,每行三个正整数 l,r,d(1≤l≤r≤n,1≤d≤100000),表示每个问题。
Output
对于每个问题输出一行,若是倍数,输出Yes,否则输出No。
Sample Input
15 46 4 7 2 51 2 241 3 182 5 173 5 35
Sample Output
YesNoNoYes
Source
思路: 字典树。 我们知道每一个数可以由质数相乘得到,我们要查询一个区间的所有数相乘是不是d的倍数,那么就可以查询这个区间内d所需要的质数的个数是否小于区间内的数的(由质数组成的个数) 所包含的质数的个数。 那么我们就可以对序列进行处理,对于每一个数,我们进行字典树更新,字典树的叶节点表示1到1e+5,节点的值为该数组成序列数的个数(叶节点是质数才不为0,否则为0)。其他就是字典树的查询,,sum[ r ]-sum[ l-1 ].
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
typedef pair<int ,int >pii;
vector<pii> ve[N];
void init()
{
int cnt;
for(int i=2;i<N;i++)
{
int x=i;
for(int j=2;j*j<=x;j++)
{
int cnt=0;
while(x%j==0){
x/=j;
cnt++;
}
if(cnt) ve[i].push_back(make_pair(j,cnt));
}
if(x>1) ve[i].push_back(make_pair(x,1));
}
return ;
}
//int rt[N*400];
int ls[N*400],rs[N*400],sum[N*400];
int tot;
void build(int &i,int l,int r)
{
i=++tot;
sum[i]=0;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls[i],l,mid);
build(rs[i],mid+1,r);
}
void update(int &i,int pre,int l,int r,int p,int num) // 单点更新
{
i=++tot;
ls[i]=ls[pre];
rs[i]=rs[pre];
sum[i]=sum[pre]+num;
if(l==r) return ;
int mid=(l+r)>>1;
if(p<=mid) update(ls[i],ls[pre],l,mid,p,num);
else update(rs[i],rs[pre],mid+1,r,p,num);
}
int query(int L,int R,int l,int r,int lt,int rt) // 其实是单点查询 因为L==R
{
if(l>=L&&r<=R){
return sum[rt]-sum[lt];
}
int mid=(l+r)>>1;
if(R<=mid){
return query(L,R,l,mid,ls[lt],ls[rt]);
}
else if(L>mid){
return query(L,R,mid+1,r,rs[lt],rs[rt]);
}
else{
return query(L,mid,l,mid,ls[lt],ls[rt])+query(mid+1,R,mid+1,r,rs[lt],rs[rt]);
}
}
int n,m;
int a[N];
int l,r,d;
int RT[N];
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
tot=0;
memset(sum,0,sizeof(sum));
memset(ls,0,sizeof(ls));
memset(rs,0,sizeof(rs));
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(RT[0],1,N-1);
/*
for(int i=1;i<=n;i++)
{
int num=a[i];
cout<<" num "<<num<<endl;
int sz=ve[num].size();
for(int j=0;j<sz;j++){
cout<<ve[num][j].first<<" "<<ve[num][j].second<<endl;
}
}
*/
for(int i=1;i<=n;i++)
{
int num=a[i];
if(num==1){
RT[i]=RT[i-1];continue;
}
int sz=ve[num].size();
int pre=RT[i-1];
for(int j=0;j<sz;j++)
{
update(RT[i],pre,1,N-1,ve[num][j].first,ve[num][j].second);
pre=RT[i];
}
}
/*
for(int i=1;i<=n;i++){
cout<<RT[i]<<" ";
}
cout<<endl;
*/
int l,r,d;
while(m--)
{
scanf("%d %d %d",&l,&r,&d);
if(d==1){
printf("Yes\n");
continue;
}
int flag=1;
int sz=ve[d].size();
for(int j=0;j<sz;j++)
{
int cnt=query(ve[d][j].first,ve[d][j].first,1,N-1,RT[l-1],RT[r]);
if(cnt<ve[d][j].second) flag=0;
}
if(flag) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
当然还有另外一种比较简单的方法,就是二分。在这里我们对于序列中的每个数,我们计算出他是由哪些质数组成,在这些质数的数组中加入该数的下标,这样在查询的时候我们就可以二分d的每个组成质数是否在l到r内被满足。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e5+5;
vector< int >ve[N];
void work(int x,int index)
{
for(int i=2;i*i<=x;i++)
{
while(x%i==0){
x=x/i;
ve[i].push_back(index);
}
}
if(x>1) ve[x].push_back(index);
return ;
}
int jud(int l,int r,int i)
{
return upper_bound(ve[i].begin(),ve[i].end(),r)-lower_bound(ve[i].begin(),ve[i].end(),l);
}
int main()
{
int T;
int n,m;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=0;i<N;i++) ve[i].clear();
int num;
for(int i=1;i<=n;i++)
{
scanf("%d",&num);
work(num,i);
}
int l,r,d;
while(m--)
{
scanf("%d %d %d",&l,&r,&d);
int flag=1;
for(int i=2;i*i<=d;i++)
{
int cnt=0;
while(d%i==0){
d/=i;
cnt++;
}
int nowcnt=jud(l,r,i);
if(nowcnt<cnt){
flag=0; break;
}
}
if(d>1){
int nowcnt=jud(l,r,d);
if(nowcnt<1){
flag=0;
}
}
if(flag) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}