1. 程式人生 > >【bzoj5016】[Snoi2017]一個簡單的詢問 莫隊算法

【bzoj5016】[Snoi2017]一個簡單的詢問 莫隊算法

bsp math 輸出 load 存在 不能 int 註意 sort

題目描述

給你一個長度為N的序列ai,1≤i≤N和q組詢問,每組詢問讀入l1,r1,l2,r2,需輸出 技術分享 get(l,r,x)表示計算區間[l,r]中,數字x出現了多少次。

輸入

第一行,一個數字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的最大值

輸出

對於每組詢問,輸出一行一個數字,表示答案

樣例輸入

5
1 1 1 1 1
2
1 2 3 4
1 1 4 4

樣例輸出

4
1


題解

莫隊算法

(為了方便,以下使用$S_x(l,r)$代替$get(l,r,x)$)

題目一眼莫隊,不過由於一個詢問有4個參數,不能直接處理。

考慮將詢問拆成前綴相減的形式,即:

$\ \ \ \ \sum\limits_xS_x(l_1,r_1)·S_x(l_2,r_2)\\=\sum\limits_{x}(S_x(1,r_1)-S_x(1,l_1-1))·(S_x(1,r_2)-S_x(1,l_2-1))\\=\sum\limits_{x}(S_x(1,r_1)·S_x(1,r_2)-S_x(1,l_1-1)·S_x(1,r_2)--S_x(1,r_1)·S_x(1,l_2-1)+S_x(1,l_1-1)·S_x(1,l_2-1))\\=Q(r_1,r_2)-Q(l_1-1,r_2)-Q(r_1,l_2-1)+Q(l_1-1,l_2-1)$

其中:

$Q(a,b)=\sum\limits_{x}S_x(1,a)·S_x(1,b)$

於是就可以把每個詢問拆成4個,使用莫隊算法分別計算對每個答案的貢獻即可。

註意當$a$或$b$中某一個為0時的情況需要過濾掉,否則會加入不存在的位置導致掛掉。

時間復雜度$O(n\sqrt{4m})$

#include <cmath>
#include <cstdio>
#include <algorithm>
#define N 50010
using namespace std;
typedef long long ll;
int si , v[N] , cl[N] , cr[N] , tot;
ll ans[N];
struct data
{
	int l , r , flag , id;
	data() {}
	data(int a , int b , int c , int d) {l = min(a , b) , r = max(a , b) , flag = c , id = d;}
	bool operator<(const data &a)const {return (l - 1) / si == (a.l - 1) / si ? r < a.r : (l - 1) / si < (a.l - 1) / si;}
}a[N << 2];
int main()
{
	int n , m , i , x1 , y1 , x2 , y2 , lp = 0 , rp = 0;
	ll now = 0;
	scanf("%d" , &n) , si = (int)sqrt(n);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
	scanf("%d" , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d%d%d%d" , &x1 , &y1 , &x2 , &y2) , a[++tot] = data(y1 , y2 , 1 , i);
		if(x1 > 1) a[++tot] = data(x1 - 1 , y2 , -1 , i);
		if(x2 > 1) a[++tot] = data(y1 , x2 - 1 , -1 , i);
		if(x1 > 1 && x2 > 1) a[++tot] = data(x1 - 1 , x2 - 1 , 1 , i);
	}
	sort(a + 1 , a + tot + 1);
	for(i = 1 ; i <= tot ; i ++ )
	{
		while(lp < a[i].l) lp ++ , now += cr[v[lp]] , cl[v[lp]] ++ ;
		while(rp < a[i].r) rp ++ , now += cl[v[rp]] , cr[v[rp]] ++ ;
		while(lp > a[i].l) cl[v[lp]] -- , now -= cr[v[lp]] , lp -- ;
		while(rp > a[i].r) cr[v[rp]] -- , now -= cl[v[rp]] , rp -- ;
		ans[a[i].id] += a[i].flag * now;
	}
	for(i = 1 ; i <= m ; i ++ ) printf("%lld\n" , ans[i]);
	return 0;
}

【bzoj5016】[Snoi2017]一個簡單的詢問 莫隊算法