bzoj 5016: [Snoi2017]一个简单的询问

Posted qt666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 5016: [Snoi2017]一个简单的询问相关的知识,希望对你有一定的参考价值。

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
Sample Output

4
1
HINT

Source

 

这个鬼题搞了好久啊,数据范围给人一种莫队的感觉;

我们考虑把询问转化为前缀和形式:

 技术分享

然后我们考虑如何解决:

技术分享

假设r1<=r2,那么我们得到下面一个式子:

 技术分享

然后我们记:

get(1,r1,x)=ret[x],

get(1,r2,x)-get(1,r1+1,x)为tmp[x] (tmp[x]为区间(r1,r2]中x的数目,这是经典莫队问题);

那么答案变为:

技术分享

然后我们把r1记为l,r2记为r来考虑莫队算法O(1)转移:

如果左端点往左移,设移动后的颜色为x:

那么ret[x]--,tmp[x]++,颜色x的贡献值变为 (ret[x]-1)^2+(tmp[x]+1)*(ret[x]-1);

那么变化为-ret[x]-tmp[x];

如果右端点往右移,设移动后的颜色为x:

那么tmp[x]++,变化值为+ret[x];

其余移动类似,然后考虑到边界条件,需要把询问变为左开右闭,(即l++)

//MADE BY QT666
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=500050;
struct data{
    int l,r,id,flg;
}q[N];
int a[N],ret[N],tmp[N],n,pos[N],block,tot,m;
ll ans[N],Ans;
bool cmp(const data &a,const data &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    else return pos[a.l]<pos[b.l];
}
void addl(int x){Ans+=(-ret[x]-tmp[x]);ret[x]--,tmp[x]++;}
void dell(int x){Ans+=(ret[x]+tmp[x]);ret[x]++;tmp[x]--;}
void addr(int x){Ans+=ret[x];tmp[x]++;}
void delr(int x){Ans+=(-ret[x]);tmp[x]--;}
void Modui(){
    Ans=0;
    for(int l=1,r=0,i=1;i<=tot;i++){
	while(l>q[i].l) l--,addl(a[l]);
	while(r<q[i].r) r++,addr(a[r]);
	while(l<q[i].l) dell(a[l]),l++;
	while(r>q[i].r) delr(a[r]),r--;
	ans[q[i].id]+=Ans*q[i].flg;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
	int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
	q[++tot]=(data){r1,r2,i,1};
	q[++tot]=(data){r1,l2-1,i,-1};
	q[++tot]=(data){l1-1,r2,i,-1};
	q[++tot]=(data){l1-1,l2-1,i,1};
    }
    for(int i=1;i<=tot;i++){
	if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
	q[i].l++;
    }
    block=sqrt(n);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    sort(q+1,q+1+tot,cmp);Modui();
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0; 
}




















以上是关于bzoj 5016: [Snoi2017]一个简单的询问的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 5016: [Snoi2017]一个简单的询问

bzoj5016[Snoi2017]一个简单的询问 莫队算法

SNOI2017(BZOJ5015~5018)泛做

[LOJ#2255][BZOJ5017][Snoi2017]炸弹

BZOJ5015[Snoi2017]礼物 矩阵乘法

BZOJ5018[Snoi2017]英雄联盟 背包