数列互质
题意:给定长度为n的数列,m次询问,每次询问给定三个数l,r,k,询问在区间[l,r]内有多少个数的次数与k是互质的
1<=n,m<=50000
1<=l[i],r[i].k[i]<=n
询问区间数的次数,可以想到用莫队维护,用siz[x]统计数x出现的次数,cnt[x]统计出现次数为x的数有几个。这些在add,del函数中都可以O(1)实现,因为要统计有多少数与k互质,所以如果我们将出现次数从1到n枚举那么时间复杂度是O(mnlogn),妥妥T。
对此我们是否可以将次数也优化一下呢?
可以看出,我们可以统计出现次数<=
n
\sqrt n
n,最多统计
n
\sqrt n
n次,ans+=cnt[i]即可;对于出现次数>
n
\sqrt n
n的来说,其数字最多出现
n
\sqrt n
n次,因为若出现比
n
\sqrt n
n还多那么数列的长度肯定>n,所以我们可以对于出现
n
\sqrt n
n次的数单独来维护。由于del的时候会出现出现次数为
n
+
1
\sqrt n+1
n+1,变成
n
\sqrt n
n的情况,所以我们可以将其用栈来维护,每次如果这个点在栈中并且出现次数<=
n
\sqrt n
n,我们就在一次询问中统计答案时将其剔除即可。由于可能一个数加两次,所以我们add的时候来一个遍历vis记录是否在栈中,如果不在栈中我们才将其入栈,所以栈里最多
n
\sqrt n
n个数。
时间复杂度: O(
n
n
l
o
g
n
n\sqrt n logn
nnlogn) log是求GCD的复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+10;
int sq,a[maxn],res[maxn];
struct node{
int l,r,id,k;
bool friend operator<(node x1,node y1)
{
int x=x1.l/sq,y=y1.l/sq;
if(x==y) return x1.r<y1.r;
return x<y;
}
}b[maxn];
int siz[maxn],cnt[maxn],vis[maxn];
stack<int>st;
void add(int x)
{
x=a[x];
cnt[siz[x]]--; siz[x]++;
cnt[siz[x]]++;
if(siz[x]==sq+1) {
if(!vis[x]) {
vis[x]=1;
st.push(x);
}
}
}
void del(int x)
{
x=a[x];
cnt[siz[x]]--;siz[x]--;
cnt[siz[x]]++;
}
ll gcd(ll a,ll b)
{
if(!b) return a;
return gcd(b,a%b);
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];sq=sqrt(n);
for(int i=1;i<=m;i++)
cin>>b[i].l>>b[i].r>>b[i].k,b[i].id=i;
sort(b+1,b+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
while(l>b[i].l) add(--l);
while(l<b[i].l) del(l++);
while(r>b[i].r) del(r--);
while(r<b[i].r) add(++r);
for(int j=1;j<=sq;j++)
if(gcd(j,b[i].k)==1)
res[b[i].id]+=cnt[j];
stack<int>st1;swap(st,st1);
while(!st.empty())
st.pop();
while(st1.size()) //sqrt(n)
{
int k=st1.top();st1.pop();
if(siz[k]>=sq+1) {
st.push(k);
if(gcd(b[i].k,siz[k])==1)
res[b[i].id]++;
}
else vis[k]=0;
}
}
for(int i=1;i<=m;i++)
cout<<res[i]<<endl;
return 0;
}