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

原创 2018年04月16日 11:39:01

Description

给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出i=1get(l1,r1,x)get(l2,r2,x)
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

分析

我们要求的实际上就是区间[l1,r1]中的每个数在区间[l2,r2]中出现次数的和。
考虑按位置分块,预处理f[i,j]表示第i块中的每个数在[1,j]中出现次数的和。
询问的时候,整块的可以O(1)查询,那么多出来的怎么办呢?
如果拿可持久化线段树来查询的话,复杂度要多一个logn
注意到现在的瓶颈在于如何快速求[l2,r2]中某个数的出现次数,那么只要把询问按照[l2,r2]莫队一下就好了。
时间复杂度O(nn)

代码

#include <bits/stdc++.h>

const int N=50005;
const int M=250;

typedef long long ll;

int n,tot,bel[N],B,sta[M],end[M],t[N],a[N];
ll f[M][N],ans[N];

struct Data
{
    int l1,r1,l2,r2,id;
}q[N];

int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

void preWork()
{
    B = sqrt(n);
    for (int i = 1; i <= n; i++)
    {
        bel[i] = (i + B - 1) / B;
        if (!sta[bel[i]]) 
            sta[bel[i]] = i;
        end[bel[i]] = i;
    }
    for (int i = 1; i <= bel[n]; i++)
    {
        for (int j = sta[i]; j <= end[i]; j++) 
            t[a[j]]++;
        for (int j = 1; j <= n; j++) 
            f[i][j] = f[i][j - 1] + t[a[j]];
        for (int j = sta[i]; j <= end[i]; j++) 
            t[a[j]] = 0;
    }
}

bool cmp(Data a,Data b)
{
    return bel[a.l2] < bel[b.l2] || bel[a.l2] == bel[b.l2] && a.r2 < b.r2;
}

ll query(int l1,int r1,int l2,int r2)
{
    ll ans = 0;
    if (bel[l1] == bel[r1])
    {
        for (int i = l1; i <= r1; i++) 
            ans += t[a[i]];
        return ans;
    }
    for (int i = l1; i <= end[bel[l1]]; i++) 
        ans += t[a[i]];
    for (int i = sta[bel[r1]]; i <= r1; i++) 
        ans += t[a[i]];
    for (int i = bel[l1] + 1; i < bel[r1]; i++)
        ans += f[i][r2] - f[i][l2 - 1];
    return ans;
}

void solve()
{
    for (int l = 1, r = 0, i = 1; i <= tot; i++)
    {
        for (; r < q[i].r2; r++) 
            t[a[r+1]]++;
        for (; l > q[i].l2; l--) 
            t[a[l-1]]++;
        for (; r > q[i].r2; r--) 
            t[a[r]]--;
        for (; l < q[i].l2; l++) 
            t[a[l]]--;
        ans[q[i].id] = query(q[i].l1,q[i].r1,q[i].l2,q[i].r2);
    }
}

int main()
{
    n = read();
    for (int i = 1; i <= n; i++) 
        a[i] = read();
    preWork();
    tot = read();
    for (int i = 1; i <= tot; i++) 
        q[i].l1 = read(), q[i].r1 = read(), q[i].l2 = read(), q[i].r2 = read(), q[i].id = i;
    std::sort(q + 1, q + tot + 1, cmp);
    solve(); 
    for (int i = 1; i <= tot; i++) 
        printf("%d\n",ans[i]);
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ypxrain/article/details/79958569

【bzoj5016】[Snoi2017]一个简单的询问(莫队)

宠辱不惊,看庭前花开花落;去留无意,望天上云卷云舒
  • Blue_CuSO4
  • Blue_CuSO4
  • 2017-09-12 08:02:09
  • 308

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

题意:求∑maxx=0get(l1,r1,x)∗get(l2,r2,x)\sum^{max}_{x=0}get(l1,r1,x)*get(l2,r2,x)题解:分块+线段树。 一开始傻傻的想分颜色块...
  • qq_36808030
  • qq_36808030
  • 2017-09-05 13:34:40
  • 272

loj 2254 bzoj 5016 「SNOI2017」一个简单的询问

loj 2254 bzoj 5016 「SNOI2017」一个简单的询问
  • Rising_shit
  • Rising_shit
  • 2018-01-12 15:07:51
  • 163

bzoj5016: [Snoi2017]一个简单的询问

分块
  • qq_36797743
  • qq_36797743
  • 2017-09-05 13:26:33
  • 298

bzoj 5016: [Snoi2017]一个简单的询问(莫队)

5016: [Snoi2017]一个简单的询问 Time Limit: 30 Sec  Memory Limit: 512 MB Submit: 87  Solved: 64 [Submit][...
  • Jaihk662
  • Jaihk662
  • 2017-10-07 17:44:52
  • 174

BZOJ 5016: [Snoi2017]一个简单的询问 莫队算法

5016: [Snoi2017]一个简单的询问Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 193  Solved: 147[Submit][Stat...
  • BlackJack_
  • BlackJack_
  • 2018-02-26 19:14:38
  • 53

bzoj5016 [Snoi2017]一个简单的询问

Description 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次。Inpu...
  • wu_tongtong
  • wu_tongtong
  • 2017-09-10 17:26:41
  • 170

bzoj 5016: [Snoi2017]一个简单的询问 莫队算法+分块

题意 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2,需输出 get(l,r,x)表示计算区间[l,r]中,数字x出现了多少次。 N,Q≤50000 ...
  • qq_33229466
  • qq_33229466
  • 2018-03-26 15:56:18
  • 17

[Snoi2017]一个简单的询问

Description 给你一个长度为N的序列ai,1≤i≤N和q组询问,每组询问读入l1,r1,l2,r2, 需输出Sigma(get(l1,r1,x)*get(l2,r2,x))(x&amp;...
  • xgc_woker
  • xgc_woker
  • 2018-04-18 16:06:30
  • 11

[BZOJ]5018: [Snoi2017]英雄联盟 DP

DP
  • baidu_36797646
  • baidu_36797646
  • 2017-09-02 18:34:08
  • 406
收藏助手
不良信息举报
您举报文章:BZOJ 5016: [Snoi2017]一个简单的询问
举报原因:
原因补充:

(最多只允许输入30个字)