BUPT2017 wintertraining(15) #4B
Gym - 101138D
题意
a数组大小为n。(1 ≤ n ≤ 50 000) (1 ≤ q ≤ 50 000)(1 ≤ ai ≤ n)
q个查询,询问两个区间相同的数有多少对。
题解
[sl,sr]和[tl,tr]区间相同的数的对数可以用\(f[sl,tr]-f[sl,tl]-f[sr,tr]+f[sr,tr]\)计算。\(f[l,r]\)为区间[l,r]内相同的数的对数。
对于每个询问,记录需要计算的f的区间,然后按l/sqrt(n)为第一关键字,r为第二关键字排序。
如果计算完f[l,r],那么计算f[l1,r1]时,可以由[l,r]区间转移到[l1,r1]区间,相当于移动左右端点的指针。
随便写写的时间复杂度分析(n为计算的区间个数):
pos[i]记录区间i的第一关键字。
相邻的两个计算区间(排序后),若pos相同,左指针移动最远\(\sqrt n\)步,最坏情况就是n个区间都移动这么多步,总的最多\(n\sqrt n\)步。
若pos不同,总的最多是\(n\)步。
pos相同的所有区间,右指针最多共移动n步(1到n),共\(\sqrt n\)个pos值,总的最多移动\(n\sqrt n\)步。
pos不同时,右指针最多移动n步(n到1),共\(\sqrt n\)个pos,总的最多移动\(n\sqrt n\)步。
因为计算所有区间的过程,左指针最多移动\(n\sqrt n\)步,右指针最多移动了\(n\sqrt n\)步。因此复杂度是\(O(n\sqrt n)\)。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#define N 50005
#define ll long long
using namespace std;
int n,m,a[N],qs,pos[N];
ll ans[N],s[N];
struct node{int l,r,d;}p[N<<2];
bool cmp(node a,node b){
return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;
}
void add(int p,ll &f){
f+=s[a[p]]++;
}
void sub(int p,ll &f){
f-=--s[a[p]];
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1,j=1;i<=n;i++){
if(i%(int)sqrt(n)==0)j++;
pos[i]=j;
}
scanf("%d",&qs);
for(int i=1;i<=qs;i++){
int sl,sr,tl,tr;
scanf("%d%d%d%d",&sl,&sr,&tl,&tr);
sr++;tl--;
//[sl,tr]-[sl,tl-1]-[sr+1,tr]+[sr+1,tl-1]
p[m++]=(node){sl,tr,i};p[m++]=(node){sl,tl,-i};
p[m++]=(node){sr,tr,-i};p[m++]=(node){sr,tl,i};
}
sort(p,p+m,cmp);
int L=n+1,R=n;
ll num=0;
for(int i=0;i<m;i++){
while(L<p[i].l)
sub(L++,num);
while(L>p[i].l)
add(--L,num);
while(R>p[i].r)
sub(R--,num);
while(R<p[i].r)
add(++R,num);
if(p[i].d>0)ans[p[i].d]+=num;
else ans[-p[i].d]-=num;
}
for(int i=1;i<=qs;i++)printf("%lld\n",ans[i]);
return 0;
}