Description
给你一个序列aaa,有mmm次,每次查询一个区间[l,r][l,r][l,r]。
这个区间内一共有2r−l+1−12^{r-l+1}-12r−l+1−1个非空子序列
一个子序列对答案的贡献是其去重后的和
求所有子序列的贡献的和%p\%p%p
每次的ppp不一样
Input
第一行两个数n,mn,mn,m
第二行nnn个数表示序列aaa
后面mmm行每行三个数l,r,pl,r,pl,r,p表示查询区间[l,r][l,r][l,r],模数是ppp
(1≤n,m,ai≤105,1≤p≤109)(1\le n,m,a_i\le 10^5,1\le p\le 10^9)(1≤n,m,ai≤105,1≤p≤109)
Output
对于每个查询输出一行一个数表示答案
Sample Input
5 5
1 2 2 3 4
1 2 233333
2 3 333333
1 5 203
3 5 15
2 4 8
Sample Output
6
6
176
6
0
Solution
假设数字xxx在区间[l,r][l,r][l,r]中出现了yyy次,令len=r−l+1len=r-l+1len=r−l+1为该区间长度,那么包含该数字的子序列个数为2len−y⋅(2y−1)2^{len-y}\cdot (2^y-1)2len−y⋅(2y−1),进而xxx对该区间的贡献为x⋅2len−y⋅(2y−1)=x⋅(2len−2len−y)x\cdot 2^{len-y}\cdot (2^y-1)=x\cdot (2^{len}-2^{len-y})x⋅2len−y⋅(2y−1)=x⋅(2len−2len−y)
从该贡献值可以看出x,yx,yx,y两者独立,我们只需要统计出现次数为yyy的数字之和即可,如此的好处是不同的yyy值至多O(n)O(\sqrt{n})O(n)个,用一个线性链表来维护这不同的n\sqrt{n}n个出现次数,维护每个出现次数的数字个数和数字之和,那么每次查询的结果可以写成一个n\sqrt{n}n项的求和式
由于模数不同,不能预处理222的次幂,为了加速该求和,对2n2^n2n分块为2⌊nnn⌋⋅nn+n%nn2^{\lfloor\frac{n}{nn}\rfloor\cdot nn+n\%nn}2⌊nnn⌋⋅nn+n%nn,其中nn=⌈n⌉nn=\lceil\sqrt{n}\rceilnn=⌈n⌉,每次查询前预处理20,...,2nn2^0,...,2^{nn}20,...,2nn以及2nn,22⋅nn,...,2nn⋅nn2^{nn},2^{2\cdot nn},...,2^{nn\cdot nn}2nn,22⋅nn,...,2nn⋅nn,这样就可以O(1)O(1)O(1)得到2n2^n2n
时间复杂度O(nn)O(n\sqrt{n})O(nn)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
namespace fastIO
{
#define BUF_SIZE 100000
//fread -> read
bool IOerror=0;
inline char nc()
{
static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
if(p1==pend)
{
p1=buf;
pend=buf+fread(buf,1,BUF_SIZE,stdin);
if(pend==p1)
{
IOerror=1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch)
{
return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
}
inline void read(int &x)
{
char ch;
while(blank(ch=nc()));
if(IOerror)return;
int sgn=1;
if(ch=='-')sgn=-1;
for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
x=sgn*x;
}
#undef BUF_SIZE
};
using namespace fastIO;
typedef long long ll;
const int maxn=100005;
int n,m,mm,a[maxn],num[maxn],vis[maxn],pos[maxn],ans[maxn];
ll sum[maxn];
int head,L[maxn],R[maxn];
struct node
{
int l,r,p,id;
bool operator<(const node&b)const
{
if(pos[l]!=pos[b.l])return l<b.l;
return r<b.r;
}
}q[maxn];
void insert(int x)
{
R[x]=head;L[head]=x;head=x;L[x]=0;
}
void erase(int x)
{
if(x==head)head=R[x];
else L[R[x]]=L[x],R[L[x]]=R[x];
}
void update(int x,int v)//表示对第x个元素做删除(v=-1)或者添加(v=1)
{
if(num[a[x]])
{
sum[num[a[x]]]-=a[x];
vis[num[a[x]]]--;
if(!vis[num[a[x]]])erase(num[a[x]]);
}
num[a[x]]+=v;
if(num[a[x]])
{
sum[num[a[x]]]+=a[x];
vis[num[a[x]]]++;
if(vis[num[a[x]]]==1)insert(num[a[x]]);
}
}
int p,f[1000],g[1000];
int add(int x,int y)
{
x+=y;
if(x>=p)x-=p;
return x;
}
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/p*p;
}
void init(int n)
{
f[0]=1;
for(int i=1;i<=mm;i++)f[i]=add(f[i-1],f[i-1]);
g[0]=1;
for(int i=1;i<=n/mm;i++)g[i]=mul(g[i-1],f[mm]);
}
int Pow(int n)
{
return mul(g[n/mm],f[n%mm]);
}
int query(int l,int r,int tp)
{
p=tp;
init(r-l+1);
int ans=0;
for(int i=head;i;i=R[i])
ans=add(ans,mul(sum[i]%p,add(Pow(r-l+1),p-Pow(r-l+1-i))));
return ans;
}
int main()
{
read(n);read(m);
mm=(int)sqrt(n)+1;
for(int i=1;i<=n;i++)
{
read(a[i]);
pos[i]=(i-1)/mm+1;
}
for(int i=0;i<m;i++)
{
read(q[i].l);read(q[i].r);read(q[i].p);
q[i].id=i;
}
sort(q,q+m);
int l=1,r=0;
for(int i=0;i<m;i++)
{
while(r<q[i].r)update(r+1,1),r++;
while(r>q[i].r)update(r,-1),r--;
while(l<q[i].l)update(l,-1),l++;
while(l>q[i].l)update(l-1,1),l--;
ans[q[i].id]=query(q[i].l,q[i].r,q[i].p);
}
for(int i=0;i<m;i++)printf("%d\n",ans[i]);
return 0;
}