應該很容易想到用莫隊做,然後那?我們現在已經知道了一個區間內各個數出現的次數,及其次數出現的次數,我們應該怎麼算他互質的數的個數?我們先要知道這麼一個事實,
一個區間內次數的不同最多是sqrt(n)個,,所以整個算法的複雜度是,,nsqrt(n)(莫隊的)+msqrt(n)*(求互質的複雜度)
這個(求互質的複雜度),,一般有三種方法,,第一種直接logn求gcd。。第二種現將k的質因子找到,然後對於每個次數試除k的質因子看看能不能整除。。。第三種是用玄學O(1)的gcd。。
我三種都寫了一下,,,最快的是第二種,第一種和第三種速度差不多。。
我的解釋是O(1)的gcd常數過大,並且這道題的數據太小才5*10^4,對於第一種方法最多在14次之內求出gcd,可以認爲常數爲14。對於第二種質因子數最多才7個,常數爲7。對於第三種,常數不是很算的清,但是可以知道很大。所以O(1)gcd的效果不明顯。。。如果數據爲100W,,第三種的速度就體現了,因爲第三種的常數是固定的。前兩種的常數和數據範圍成正比。
下面就貼一個第三種的寫法吧,,前兩種大家應該很熟悉了
題外話:這個loj很不錯啊,
#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<map>
#include<set>
#include<bitset>
#include<unordered_map>
#include<stack>
#include<queue>
#include<string.h>
#include<cstring>
#include<vector>
#include<time.h>
#include<stdlib.h>
using namespace std;
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f3f3f
#define FIN freopen("input.txt","r",stdin)
#define mem(x,y) memset(x,y,sizeof(x))
typedef unsigned long long ULL;
typedef long long LL;
#define fuck(x) cout<<"q"<<endl;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<pair<int,int>,int> PIII;
typedef pair<int,int> PII;
const double eps=1e-8;
#define MX 111111
#define MK 444
bool isprime[MX];
int prime[MX];
int G[MK+5][MK+5];
int s[MX][3];
void gcd_init()
{
s[1][0]=s[1][1]=s[1][2]=1;
mem(isprime,1);
prime[0]=0;
for(int i=2; i<MX; i++)
{
if(isprime[i])
{
prime[++prime[0]]=i;
s[i][0]=i;
s[i][1]=s[i][2]=1;
}
for(int j=1; j<=prime[0]&&i*prime[j]<MX; j++)
{
int v=i*prime[j];
for(int k=0; k<3; k++)s[v][k]=s[i][k];
if(s[v][0]*prime[j]<MK)s[v][0]*=prime[j];
else if(s[v][1]*prime[j]<MK)s[v][1]*=prime[j];
else s[v][2]*=prime[j];
isprime[v]=0;
if(i%prime[j]==0)break;
}
}
for(int i=1; i<MK; i++)
for(int j=0; j<i; j++)if(!j)G[i][j]=i;
else G[i][j]=G[j][i]=G[j][i%j];
}
int Gcd(int x,int y)
{
if(!x||!y)return x+y;
if(x<MK&&y<MK)return G[x][y];
int ans=1,d;
for(int i=0; i<3; i++)
{
if(s[x][i]==1)continue;
else if(s[x][i]<MK)d=G[s[x][i]][y%s[x][i]];
else if(y%s[x][i]==0)d=s[x][i];
else d=1;
y/=d;
ans*=d;
}
return ans;
}
int n,m;
int block;
int w[MX];
struct Q
{
int l,r,k,id;
bool operator <(const Q a)const
{
if(l/block!=a.l/block)return l/block<a.l/block;
return r/block<a.r/block;
}
} q[MX];
int ans[MX];
int cnt[MX];
int num[MX];
int path[MX];
void Delete(int v)
{
num[cnt[v]--]--;
path[++path[0]]=cnt[v];
num[cnt[v]]++;
}
void Insert(int v)
{
num[cnt[v]++]--;
path[++path[0]]=cnt[v];
num[cnt[v]]++;
}
int g[MX];
int vis[MX];
int solve(int k)
{
if(k==1)k=57223;
g[0]=0;
int ans=0;
int tmp=0;
for(int i=1; i<=path[0]; i++)
if(num[path[i]]&&vis[path[i]]==0)
{
vis[path[i]]=1;
int flag=1;
// for(int j=1; j<=g[0]; j++)if(path[i]%g[j]==0)flag=0;
if(Gcd(k,path[i])==1)ans+=num[path[i]];
path[++tmp]=path[i];
}
for(int i=1; i<=tmp; i++)vis[path[i]]=0;
path[0]=tmp;
return ans;
}
inline int read()
{
int ret=0,c,f=1;
for(c=getchar(); !(isdigit(c)||c=='-'); c=getchar());
if(c=='-') f=-1,c=getchar();
for(; isdigit(c); c=getchar()) ret=ret*10+c-'0';
if(f<0) ret=-ret;
return ret;
}
int main()
{
gcd_init();
// prime_init();
FIN;
while(cin>>n>>m)
{
mem(cnt,0);
block=sqrt(n+0.5);
for(int i=1; i<=n; i++)w[i]=read();
for(int i=1; i<=m; i++)
{
q[i].l=read(),q[i].r=read(),q[i].k=read();
q[i].id=i;
}
sort(q+1,q+1+m);
int l=0,r=0;
cnt[0]=1;
num[1]=1;
path[0]=1;
path[1]=0;
for(int i=1; i<=m; i++)
{
while(l<q[i].l)Delete(w[l++]);
while(l>q[i].l)Insert(w[--l]);
while(r<q[i].r)Insert(w[++r]);
while(r>q[i].r)Delete(w[r--]);
ans[q[i].id]=solve(q[i].k);
}
for(int i=1; i<=m; i++)printf("%d\n",ans[i]);
}
return 0;
}