一个简单的询问(莫队)

5 篇文章 0 订阅
2 篇文章 0 订阅

题目(4000ms)

描述
给你一个长度为 N N N的序列 a i a_i ai 1 ≤ i ≤ N 1\leq i \leq N 1iN,和q组询问,每组询问读入 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_2 l1,r1,l2,r2,输出
∑ x = 0 ∞ g e t ( l 1 , r 1 , x ) ∗ g e t ( l 2 , r 2 , x ) \sum_{x=0}^\infty get(l_1,r_1,x)*get(l_2,r_2,x) x=0get(l1,r1,x)get(l2,r2,x)
g e t ( l , r , x ) get(l,r,x) get(l,r,x)表示计算区间 [ l , r ] [l,r] [l,r]中,数 x x x出现了多少次
输入
第一行,一个数字 N N N,表示序列长度。
第二行, N N N个数,表示 a 1 ∼ a n a_1 \sim a_n a1an
第三行,一个数字 Q Q Q,表示询问个数。
4 ∼ Q + 3 4 \sim Q+3 4Q+3行,每行四个数字 l 1 , r 1 , l 2 , r 2 l_1, r_1,l_2,r_2 l1,r1,l2,r2,表示询问。
输出
对于每组询问,输出一行一个数字,表示答案。
范围
N , Q ≤ 5 ∗ 1 0 4 , 1 ≤ a i ≤ N , 1 ≤ l 1 ≤ r 1 ≤ N , 1 ≤ l 2 ≤ r 2 ≤ N N,Q\leq 5*10^4,1\leq a_i \leq N,1\leq l_1 \leq r_1 \leq N, 1\leq l_2 \leq r_2 \leq N N,Q5104,1aiN,1l1r1N,1l2r2N
样例

样例输入
5
1 1 1 1 1
2
1 2 3 4
1 1 4 4
样例输出
4
1

思路

其实也是板题,既然它的询问有两个区间,那我们也搞两个 c l , c r , n u m cl, cr, num cl,cr,num
n u m [ i ] [ 1 ] ± num[i][1]\pm num[i][1]±,则 s u m ± = n u m [ i ] [ 0 ] sum\pm=num[i][0] sum±=num[i][0]
n u m [ i ] [ 0 ] ± num[i][0]\pm num[i][0]±,则 s u m ± = n u m [ i ] [ 1 ] sum\pm=num[i][1] sum±=num[i][1]

没了


Code
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define M 50005
#define LL long long

int n, m, bl;

int a[M], cl[2]={1,1}, cr[2];
LL num[M][2];

unsigned long long sum;
LL ans[M];

struct node{
    int l1, r1, l2, r2, id;

    bool operator < (const node &b)const{
        if( (b.l1/bl) == (l1/bl) &&  r1 != b.r1 ){
            if( (l1/bl)&1 )
                return r1 > b.r1;
            else return r1 < b.r1;
        }
        else if( (b.l2/bl) == (l2/bl) ){
            if( (l2/bl)&1 )
                return r2 > b.r2;
            else return r2 < b.r2;
        }
        return l1 < b.l1;
    }

}q[M];

inline void re(int x, int i){
    num[a[x]][i]--;
    sum -= num[a[x]][i^1];
}

inline void add(int x, int i){
    num[a[x]][i]++;
    sum += num[a[x]][i^1];
}

inline void mv(int l, int r, int i){
    while( cl[i] < l )
        re(cl[i]++, i);
    while( cl[i] > l )
        add(--cl[i], i);
    while( cr[i] < r )
        add(++cr[i], i);
    while( cr[i] > r )
        re(cr[i]--, i);
}

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 ++){
        scanf("%d%d%d%d", &q[i].l1, &q[i].r1, &q[i].l2, &q[i].r2);
        q[i].id = i;
    }
    bl = n/sqrt(m*2/3*1.0);
    sort(q+1, q+1+m);
    for(int i = 1; i <= m; i ++){
        mv(q[i].l1, q[i].r1, 0);
        mv(q[i].l2, q[i].r2, 1);
        ans[q[i].id] = sum;
    }
    for(int i = 1; i <= m; i ++)
        printf("%lld\n", ans[i]);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值