Newcoder 58 F.序列查询(莫队算法+分块+链表)

186 篇文章 0 订阅
11 篇文章 0 订阅

Description

给你一个序列 a a a,有 m m m次,每次查询一个区间 [ l , r ] [l,r] [l,r]

这个区间内一共有 2 r − l + 1 − 1 2^{r-l+1}-1 2rl+11个非空子序列

一个子序列对答案的贡献是其去重后的和

求所有子序列的贡献的和 % p \%p %p

每次的 p p p不一样

Input

第一行两个数 n , m n,m n,m

第二行 n n n个数表示序列 a a a

后面 m m m行每行三个数 l , r , p l,r,p l,r,p表示查询区间 [ l , r ] [l,r] [l,r],模数是 p p p

( 1 ≤ n , m , a i ≤ 1 0 5 , 1 ≤ p ≤ 1 0 9 ) (1\le n,m,a_i\le 10^5,1\le p\le 10^9) (1n,m,ai105,1p109)

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

假设数字 x x x在区间 [ l , r ] [l,r] [l,r]中出现了 y y y次,令 l e n = r − l + 1 len=r-l+1 len=rl+1为该区间长度,那么包含该数字的子序列个数为 2 l e n − y ⋅ ( 2 y − 1 ) 2^{len-y}\cdot (2^y-1) 2leny(2y1),进而 x x x对该区间的贡献为 x ⋅ 2 l e n − y ⋅ ( 2 y − 1 ) = x ⋅ ( 2 l e n − 2 l e n − y ) x\cdot 2^{len-y}\cdot (2^y-1)=x\cdot (2^{len}-2^{len-y}) x2leny(2y1)=x(2len2leny)

从该贡献值可以看出 x , y x,y x,y两者独立,我们只需要统计出现次数为 y y y的数字之和即可,如此的好处是不同的 y y y值至多 O ( n ) O(\sqrt{n}) O(n )个,用一个线性链表来维护这不同的 n \sqrt{n} n 个出现次数,维护每个出现次数的数字个数和数字之和,那么每次查询的结果可以写成一个 n \sqrt{n} n 项的求和式

由于模数不同,不能预处理 2 2 2的次幂,为了加速该求和,对 2 n 2^n 2n分块为 2 ⌊ n n n ⌋ ⋅ n n + n % n n 2^{\lfloor\frac{n}{nn}\rfloor\cdot nn+n\%nn} 2nnnnn+n%nn,其中 n n = ⌈ n ⌉ nn=\lceil\sqrt{n}\rceil nn=n ,每次查询前预处理 2 0 , . . . , 2 n n 2^0,...,2^{nn} 20,...,2nn以及 2 n n , 2 2 ⋅ n n , . . . , 2 n n ⋅ n n 2^{nn},2^{2\cdot nn},...,2^{nn\cdot nn} 2nn,22nn,...,2nnnn,这样就可以 O ( 1 ) O(1) O(1)得到 2 n 2^n 2n

时间复杂度 O ( n n ) 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值