P5268 [SNOI2017] 一个简单的询问
P5268 [SNOI2017] 一个简单的询问
题目描述
给你一个长度为 \(N\) 的序列 \(a_i\),\(1\leq i\leq N\),和 \(q\) 组询问,每组询问读入 \(l_1,r_1,l_2,r_2\),需输出
\[\sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\times \text{get}(l_2,r_2,x)
\]
$ \text{get}(l,r,x)$ 表示计算区间 \([l,r]\) 中,数字 \(x\) 出现了多少次。
数据范围
对于 \(100\%\) 的数据,\(N,Q\leq 50000\),\(1\leq a_i\leq N\),\(1\leq l_1\leq r_1\leq N\),\(1\leq l_2\leq r_2\leq N\)。
Solution:
首先我们不难发现我们可以用一个数组 \(cnt[pos][x]\) 来维护 \([1,pos]\) 这个区间上 \(x\) 出现的次数,那么 \(\text{get}(l_1,r_1,x)\) 就可以用 \(cnt[r_1][x]-cnt[l_1-1][x]\) 来刻画。所以我们可以把原式变形:
\[ans=(f_{r1}-f{l1}) \times (f_{r2}-f{l2})
\]
\[=f_{r1}f_{r2}-f_{r1}f_{l2}-f_{l1}f_{r2}+f_{l1}f_{l2}
\]
也就是说,我们只需要分别求出这四个值然后拼在一起就好了。
然后我们考虑如何维护这四个值:
假设现在有一个区间 \([l,r]\) ,我们希望维护 \(f_lf_r\)。
当我们扩展区间至 \([l,r+1]\) 时,答案会增加 \(cnt[l][a_{r+1}]\) .但是如果这样开 \(cnt\) 的话空间复杂度会达到 \(n^2\) 我们是不能接受的。但是我们发现,我们只关心 \(l,r\) 两个点的 \(cnt\) 所以我们可以动态维护两个桶 \(cntl,cntr\) 表示区间 \([1,l],[1,r]\) 内的某种的颜色个数。对于左端点的扩展也是类似的,这里不过多赘述。
Code:
#include<bits/stdc++.h>
#define ll long long
const int N=5e4+5;
using namespace std;
int a[N],b[N],cntl[N],cntr[N],blc[N];
ll ans[N];
int n,m,S,tot;
int l,r;
ll tmp;
inline void move_l(int x)
{
if(x==1){cntl[a[++l]]++;tmp+=cntr[a[l]];}
else {cntl[a[l]]--;tmp-=cntr[a[l--]];}
}
inline void move_r(int x)
{
if(x==1){cntr[a[++r]]++;tmp+=cntl[a[r]];}
else {cntr[a[r]]--;tmp-=cntl[a[r--]];}
}
struct task{
int l,r,id,k;
bool operator <(const task &t)const{
return blc[l]==blc[t.l] ? (blc[l]&1 ? r<t.r : t.r<r) : l<t.l;
}
}t[N<<2];
void work()
{
cin>>n;S=sqrt(N);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);b[i]=a[i];
blc[i]=i/S;
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+1+n,a[i])-b;
}
cin>>m;
for(int i=1,l,r,L,R;i<=m;i++)
{
scanf("%d%d%d%d",&l,&r,&L,&R);
l--,L--;
t[++tot]={r,R,i,1};
t[++tot]={r,L,i,-1};
t[++tot]={l,R,i,-1};
t[++tot]={l,L,i,1};
}
for(int i=1;i<=tot;i++)if(t[i].l>t[i].r)swap(t[i].l,t[i].r);
sort(t+1,t+1+tot);
for(int i=1;i<=tot;i++)
{
while(t[i].l<l)move_l(-1);
while(l<t[i].l)move_l(1);
while(r<t[i].r)move_r(1);
while(t[i].r<r)move_r(-1);
ans[t[i].id]+=tmp*t[i].k;
}
for(int i=1;i<=m;i++)
{
printf("%lld\n",ans[i]);
}
}
int main()
{
//freopen("P5268.in","r",stdin);freopen("P5268.out","w",stdout);
work();
return 0;
}

浙公网安备 33010602011771号