众所周知lxl是个毒瘤,Ynoi道道都是神仙题,题面好评
原题传送门
一看这题没有修改操作就知道这是莫队题
我博客里对莫队的简单介绍
既然是莫队,我们就要考虑每多一个数或少一个数对答案的贡献是什么
假设一个数x在区间[l,r]之间出现了y次,珂以很容易的求出该区间的长度length=r-l+1,那么包含x的区间有(2^{length-y}*(2^y-1)),(2^{length-y})表示除了这x个相同的数,其他的数取与不取的情况数,(2^y-1)表示这x个数取与不取的情况数减掉一个不取的情况,那么x对答案的贡献为(x*2^{length-y}*(2^y-1))即(x*2^{length}-x*2^{length-y})
从上式得知,x和y是相独立的,我们只需要统计出现次数为y的数字之和就行了
不同的y值最多有(sqrt n)种,用一个线性列表维护这(sqrt n)个的出现次数,维护每种出现次数的数字个数和总和,就能把查询的内容简化为一个长度为(sqrt n)的多项式
由于每次的模数p不同,所以不能预处理2的幂,为了使求和时速度更快(毒瘤),我们把(2^n)变成(2^{lfloor frac{n}{bl}
floor *bl +n mod bl}),其中(bl=lfloor sqrt n
floor)
每次只需要预处理(2^0,2^1,...,2^{bl})和(2^{bl},2^{2*bl},...,2^{bl*bl})就珂以O(1)求出二的次幂(由于取模过慢,所以我程序里用了加减乘除代替取模)
转移是(O(N sqrt N))
查询也是(O(N sqrt N))
所以总复杂度为(O(N sqrt N))
完整代码
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct query{
int l,r,p,id,bl;
}q[N];
int n,m,blocksize,a[N],ans[N];
inline bool cmp(register query a,register query b)
{
return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);
}
int pre[N],nxt[N],head=0,vis[N],cnt[N];
ll sum[N];
inline void add(register int x)
{
nxt[x]=head,pre[head]=x;
head=x,pre[x]=0;
}
inline void del(register int x)
{
if(x==head)
head=nxt[x];
else
nxt[pre[x]]=nxt[x],pre[nxt[x]]=pre[x];
}
inline void change(register int x,register int type)
{
if(cnt[a[x]])
{
sum[cnt[a[x]]]-=a[x];
if(--vis[cnt[a[x]]]==0)
del(cnt[a[x]]);
}
cnt[a[x]]+=type;
if(cnt[a[x]])
{
sum[cnt[a[x]]]+=a[x];
if(++vis[cnt[a[x]]]==1)
add(cnt[a[x]]);
}
}
int p,f[1000],g[1000];
inline int add(register int x,register int y)
{
x+=y;
if(x>=p)
x-=p;
return x;
}
inline int mul(register int x,register int y)
{
return 1ll*x*y-1ll*x*y/p*p;
}
inline void init(register int n)
{
f[0]=g[0]=1;
for(register int i=1;i<=blocksize;++i)
f[i]=add(f[i-1],f[i-1]);
for(register int i=1;i<=n/blocksize;++i)
g[i]=mul(g[i-1],f[blocksize]);
}
inline int Pow(register int n)
{
return mul(g[n/blocksize],f[n%blocksize]);
}
inline int query(register int l,register int r,register int mod)
{
p=mod;
init(r-l+1);
int ans=0;
for(register int i=head;i;i=nxt[i])
ans=add(ans,mul(sum[i]%p,add(Pow(r-l+1),p-Pow(r-l+1-i))));
return ans;
}
int main()
{
n=read(),m=read();
blocksize=(int)sqrt(n);
for(register int i=1;i<=n;++i)
a[i]=read();
for(register int i=1;i<=m;++i)
q[i].l=read(),q[i].r=read(),q[i].p=read(),q[i].id=i,q[i].bl=(q[i].l-1)/blocksize+1;
sort(q+1,q+1+m,cmp);
int l=1,r=0;
for(register int i=1;i<=m;++i)
{
while(l>q[i].l)
change(--l,1);
while(r<q[i].r)
change(++r,1);
while(l<q[i].l)
change(l++,-1);
while(r>q[i].r)
change(r--,-1);
ans[q[i].id]=query(q[i].l,q[i].r,q[i].p);
}
for(register int i=1;i<=m;++i)
write(ans[i]),puts("");
return 0;
}