F-序列查询
假设数字
x
x
x在区间[l,r]种出现y次,那么包含x的子区间个数为
2
r
−
l
+
1
−
y
⋅
(
2
y
−
1
)
2^{r-l+1-y}·(2^y-1)
2r−l+1−y⋅(2y−1),因此对询问贡献是
x
⋅
2
r
−
l
+
1
−
y
⋅
(
2
y
−
1
)
=
x
[
2
r
−
l
+
1
−
2
r
−
l
+
1
−
y
]
x·2^{r-l+1-y}·(2^y-1)=x[2^{r-l+1}-2^{r-l+1-y}]
x⋅2r−l+1−y⋅(2y−1)=x[2r−l+1−2r−l+1−y]
其中第一部分非常好维护第二部分的贡献,可以把出现次数相同的数一起维护贡献 sum[k]维护出现从次数为k的数字总和是多少。用个链表加快计算。
注意到一起区间中只有 O ( n ) O( \sqrt n ) O(n)种不同的出现次数因为 1 + 2 + . . . + n = O ( n ) 1+2+...+\sqrt n = O(n) 1+2+...+n=O(n)这是一个自然根号所以我们可以用一个均摊的莫队来维护区间可能的出现次数,从而维护区间中所有出现次数然后为了O(1)实现快速幂,我们可以每次 O ( n ) O(\sqrt n) O(n)算出 2 1 , 2 2 … 2 n m o d p 2^1,2^2…2^{\sqrt n} \mod p 21,22…2nmodp以及 2 n , 2 2 n … 2 n n m o d p 2^{\sqrt n},2^{2\sqrt n}…2^{\sqrt n\sqrt n} \mod p 2n,22n…2nnmodp
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
T res=0;T fg=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res*fg;
}
ll qmi(ll a,ll b,ll mod)
{
ll v=1;
while(b)
{
if(b&1) v=v*a%mod;
a=a*a%mod;
b>>=1;
}
return v;
}
const int N=100005;
int Bs,b[N],a[N],n,m;
struct node
{
int l,r,p,id;
bool operator<(const node&o)const
{
if(b[l]==b[o.l])
{
if(b[l]&1)
return r<o.r;
return r>o.r;
}
return b[l]<b[o.l];
}
}q[N];
int num[N],cnt[N];
ll sum[N];
int h,fr[N],ne[N];
int ans[N];
int mod;
void insert(int x)
{
ne[x]=h;
fr[h]=x;
fr[x]=0;
h=x;
}
void del(int x)
{
if(h==x) return h=ne[x],void();
ne[fr[x]]=ne[x];
fr[ne[x]]=fr[x];
}
void update(int x,int v)
{
// 出现个数为num[x]-x
if(num[x])
{
sum[num[x]]-=x;
cnt[num[x]]--;
if(!cnt[num[x]]) del(num[x]);
}
num[x]+=v;
if(num[x])
{
sum[num[x]]+=x;
cnt[num[x]]++;
if(cnt[num[x]]==1) insert(num[x]);
}
}
int add(int a,int b)
{
a+=b;
if(a>=mod) a-=mod;
return a;
}
int mul(int a,int b)
{
ll z=1ll*a*b;
return z-z/mod*mod;
}
int f[1005],g[1005];
void init(int n)
{
f[0]=1;
for(int i=1;i<=Bs;i++) f[i]=add(f[i-1],f[i-1]);
g[0]=1;
for(int i=1;i<=n/Bs;i++) g[i]=mul(g[i-1],f[Bs]);
}
int Pow(int n)
{
return mul(g[n/Bs],f[n%Bs]);
}
int query(int l,int r,int p)
{
mod=p;
int len=r-l+1;
init(n);
int ans=0;
for(int i=h;i;i=ne[i]) ans=add(ans,mul(sum[i]%p,add(Pow(len),p-Pow(len-i))));
return ans;
}
int main()
{
n=rd(),m=rd();
for(int i=1;i<=n;i++) a[i]=rd();
Bs=sqrt(n)+1;
for(int i=1;i<=n;i++) b[i]=(i-1)/Bs+1;
for(int i=1;i<=m;i++) q[i].l=rd(),q[i].r=rd(),q[i].p=rd(),q[i].id=i;
sort(q+1,q+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
while(r<q[i].r) update(a[++r],1);
while(r>q[i].r) update(a[r--],-1);
while(l<q[i].l) update(a[l++],-1);
while(l>q[i].l) update(a[--l],1);
ans[q[i].id]=query(q[i].l,q[i].r,q[i].p);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}