5016: [Snoi2017]一个简单的询问
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 132 Solved: 102
[ Submit][ Status][ Discuss]
Description
给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出
get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次。
Input
第一行,一个数字N,表示序列长度。
第二行,N个数字,表示a1~aN
第三行,一个数字Q,表示询问个数。
第4~Q+3行,每行四个数字l1,r1,l2,r2,表示询问。
N,Q≤50000
N1≤ai≤N
1≤l1≤r1≤N
1≤l2≤r2≤N
注意:答案有可能超过int的最大值
Output
对于每组询问,输出一行一个数字,表示答案
Sample Input
5
1 1 1 1 1
2
1 2 3 4
1 1 4 4
1 1 1 1 1
2
1 2 3 4
1 1 4 4
Sample Output
4
1
题解:莫队算法。
1
因为是询问一个区间内一个数的出现次数,满足前缀和性质,所以将问题转化为前缀和求解。
但每组询问有两个区间,不方便直接使用莫队求解。
对于每个x,定义sum[i]为1~i里x的出现数量,那么对于一组询问即转化为求(sum[r1]-sum[l1-1])*(sum[r2]-sum[l2-1])的值。
将上式展开得sum[r1]*sum[r2]-sum[l1-1]*sum[r2]-sum[r1]*sum[l2-1]+sum[l1-1]*sum[l2-1]。
由此可以定义一个区间[l,r]的价值为sum[l]*sum[r],将每组询问拆分为四个区间:[r1,r2],[l1-1,l2-1],[l1-1,r2],[r1,l2-1],其中第一、二个区间为正贡献,第三、四个区间为负贡献,至此可以使用莫队求解。
代码:
#include<bits/stdc++.h>
#define maxn 50005
using namespace std;
typedef long long LL;
int read()
{
char c;int sum=0,f=1;c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,q,l1,l2,r1,r2;
int a[maxn],bel[maxn],cntl[maxn],cntr[maxn];
LL ans[maxn];
struct node{
int l,r,flag,id;
node(int a,int b,int c,int d)
{
l=a;r=b;flag=c;id=d;
}
node(){}
}query[maxn<<2];
bool operator<(node a,node b)
{
return bel[a.l]!=bel[b.l]?bel[a.l]<bel[b.l]:a.r<b.r;
}
int main()
{
n=read();
int m=sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=read();
bel[i]=i/m+1;
}
q=read();
for(int i=1;i<=q;i++)
{
l1=read(),r1=read(),l2=read(),r2=read();
query[i]=node(min(r1,r2),max(r1,r2),1,i);
query[i+q]=node(min(r1,l2-1),max(r1,l2-1),-1,i);
query[i+q+q]=node(min(l1-1,r2),max(l1-1,r2),-1,i);
query[i+q+q+q]=node(min(l1-1,l2-1),max(l1-1,l2-1),1,i);
}
q<<=2;
sort(query+1,query+1+q);
int nl=0,nr=0;
LL res=0;
for(int i=1;i<=q;i++)
{
int l=query[i].l,r=query[i].r;
while(nr<r) nr++,cntr[a[nr]]++,res+=cntl[a[nr]];
while(nl<l) nl++,cntl[a[nl]]++,res+=cntr[a[nl]];
while(nr>r) res-=cntl[a[nr]],cntr[a[nr]]--,nr--;
while(nl>l) res-=cntr[a[nl]],cntl[a[nl]]--,nl--;
ans[query[i].id]+=((LL)query[i].flag*res);
}
q>>=2;
for(int i=1;i<=q;i++)
printf("%lld\n",ans[i]);
return 0;
}