1. 程式人生 > >[BZOJ5016]一個簡單的詢問

[BZOJ5016]一個簡單的詢問

可能 註意 img 不等式 元組 個數字 超過 ios width

給你一個長度為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

考慮轉化前面的式子

get(l1,r1,x)*get(l2,r2,x)
=(get(1,r1,x)-get(1,l1-1,x))*(get(1,r2,x)-get(1,l2-1,x))
=get(1,r1,x)*get(1,r2,x)-get(1,l1-1,x)*get(1,r2,x)-get(1,r1,x)*get(1,l2-1,x)+get(1,l1-1,x)*get(1,l2-1,x)

考慮如何計算get(1,a,x)*get(1,b,x)

用兩個指針l,r表示現在已經知道get(1,l,x)*get(1,r,x)的值(記在sum裏),那我們要如何求get(1,l±1,x)*get(1,r±1,x)的值呢?

以求get(1,l+1,x)*(1,r,x)舉例。

get(1,l+1,x)*get(1,r,x)

=get(1,l,x)*get(1,r,x)+a[l]在[1,r]裏的出現次數

那我們考慮用cnt1[]表示[1,l]的每個數的出現次數,cnt2[]表示[1,r]的每個數的出現次數。

那麽我們記sum=get(1,l,x)*get(1,r,x),則上式可化為sum+cnt2[a[r]],則可以在O(1)的時間復雜度內實現轉移

我們把一個詢問拆分成4個詢問,即get(1,r1,x)*get(1,r2,x),get(1,l1-1,x)*get(1,r2,x),get(1,r1,x)*get(1,l2-1,x),get(1,l1-1,x)*get(1,l2-1,x),把每個詢問用一個二元組表示[r1,r2],[l1-1,r2],[l2-1,r1],[l1-1,l2-1]然後按莫隊的方法排序,對每個詢問打一個標記ok表示它前面的符號是‘+‘還是‘-‘。

然後你可以用一個均值不等式啥的來求出最佳分塊大小,為了簡單起見,我們取分塊大小T為√n,則時間復雜度為O(4n√n)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
struct xxx{
    int l,lblock,r,id,ok;
}q[201000];
int cnt1[50100],cnt2[50100],a[50100];long long ans[50100];
bool cmp(xxx a,xxx b){return a.lblock!=b.lblock?a.lblock<b.lblock:a.r<b.r;}
int main()
{
    int n;scanf("%d",&n);int T=(int)sqrt((double)n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int Q;scanf("%d",&Q);int tot=0;
    for(int i=1;i<=Q;i++)
    {
        int l1,r1,l2,r2;scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        ++tot;q[tot].l=r1;q[tot].r=r2;q[tot].id=i;q[tot].ok=1;q[tot].lblock=(r1)/T;
        ++tot;q[tot].l=l1-1;q[tot].r=r2;q[tot].id=i;q[tot].ok=-1;q[tot].lblock=(l1-1)/T;
        ++tot;q[tot].l=l2-1;q[tot].r=r1;q[tot].id=i;q[tot].ok=-1;q[tot].lblock=(l2-1)/T;
        ++tot;q[tot].l=l1-1;q[tot].r=l2-1;q[tot].id=i;q[tot].ok=1;q[tot].lblock=(l1-1)/T;
    }
    sort(q+1,q+tot+1,cmp);
    int l=0,r=0;long long sum=0;
    for(int i=1;i<=tot;i++)
    {
        while(l<q[i].l){cnt1[a[++l]]++;sum+=cnt2[a[l]];}
        while(l>q[i].l){cnt1[a[l]]--;sum-=cnt2[a[l--]];}
        while(r<q[i].r){cnt2[a[++r]]++;sum+=cnt1[a[r]];}
        while(r>q[i].r){cnt2[a[r]]--;sum-=cnt1[a[r--]];}
        ans[q[i].id]+=sum*q[i].ok;
    }
    for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
    return 0;
}

[BZOJ5016]一個簡單的詢問