Rabbit Kingdom【HDU - 4777】【树状数组+离散化查询+区间处理思维】

题目链接


  是一道好题了,想法真的很好、很巧妙,感觉对树状数组还有很多的不了解了和欠缺,那我们先来讲解一下这道题的思路吧。

  这道题既然不需要更新节点,并且查询的范围也是固定的,并没有强制在线,所以,我们可以利用离线的思维来想,关键是在于怎么把点处理进去呢?

  这里需要些技巧了,我们可以按照一定的顺序来操作查询区间,譬如我将区间右端点按照升序排列了,然后,我们处理的方式就变成了,处理这一段区间,其实就是相当于处理这一段区间上所有的节点减去不合法节点的个数的数目就是所求的答案值。

  怎么处理不合法节点就又是一个问题了,对于区间上的点,我们先把得去处理每个区间的左右最远达到距离,但是又怎样处理左右最远达到的不合法点呢?

那么,我们就得对200000以内的所有数的质因子做下操作了,先预处理所有数的质因子们,存进每个数下:

void init()
{
    memset(prime, 0, sizeof(prime));
    cnt = 0;
    prime[0] = prime[1] = 1;
    for(int i=2; i<maxN; i++)
    {
        if(prime[i] == 0)
        {
            for(int j=2*i; j<maxN; j+=i) prime[j] = 1;
            vP[++cnt] = i;
        }
    }
    for(int i=0; i<maxN; i++) its[i].clear();
    for(int i=2; i<maxN; i++) get_have(i, i);
}

其中,get_hace()是用来得到每个数的质因子们的:

void get_have(int index, int v)
{
    int i = 1;
    while(i<=cnt && v>1)
    {
        if(v%vP[i] == 0)
        {
            its[index].push_back(vP[i]);
            while(v%vP[i] == 0) v/=vP[i];
        }
        i++;
    }
}

然后,既然每个数的质因子都已经存好了,不如接下来就对1~N这么多个数进行最远达到区间的预处理:

void init2()
{
    memset(l, 0, sizeof(l));
    memset(flag, 0, sizeof(flag));
    for(int i=0; i<=N+1; i++) RR[i].clear();
    for(int i=1; i<=N; i++)
    {
        int minn = 0;
        int len = (int)its[w[i]].size();
        for(int j=0; j<len; j++)
        {
            minn = max(minn, flag[its[w[i]][j]]);
            flag[its[w[i]][j]] = i;
        }
        l[i] = minn;
    }
    for(int i=1; i<=cnt; i++) flag[vP[i]] = N+1;
    for(int i=N; i>=1; i--)
    {
        int maxx = N + 1;
        int len = (int)its[w[i]].size();
        for(int j=0; j<len; j++)
        {
            maxx = min(maxx, flag[its[w[i]][j]]);
            flag[its[w[i]][j]] = i;
        }
        r[i] = maxx;
        RR[r[i]].push_back(i);
    }
}

哎,上面那步的时候,一开始初始化的时候没初始化彻底,造成了WA了好几发才找到错误所在(还以为自己推错了……)

  接下来,是下一个重点,也是本题的思维之所在:如何处理区间查询的问题,以及区间合法点的个数的问题,之前我们说到可以用(总的点数-不合法的点的数目)来做,那么处理不合法的点的数目应该怎么处理?对于不合法的点,有这么几种情况:

  1. 点的左端(这里的左端、右端指的是最远那个与该点冲突的节点的序号没有就是0、N+1)在查询区间内;
  2. 点的右端在查询区间内。

很显然,上述两种情况是不满足的,但是,我们这么一算,会发现会有多出来的不合法值,怎么多出来的呢?会发现,有些点的左端在区间内的情况已经放进来了,但是右端在区间内的情况也放进来了,那么这种情况不就是意味着多放进来了一种情况了吗?我们就得删除它了。

update(l[RR[i][j]], -1);    //删除这个多出来的节点

这样子不就是没有多出来的节点了吗?

并且,我们查询的区间如果不包含点的左端,会发现,根据树状数组的前缀和思想,我们会剪掉它,所以不用担心又有不合法点混入其中了。

最后,我们只需要处理区间长度减去区间内不合法点的数目即得到了每次询问区间的ans了。


代码写的不漂亮,用C++的自动优化之后就会给我T了,所以G++交吧。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 200005;
int N, Q, w[maxN], ans[maxN], l[maxN], r[maxN], flag[maxN];
struct Question
{
    int l, r, id;
    Question(int a=0, int b=0, int c=0):l(a), r(b), id(c) {}
}ques[maxN];
bool cmp(Question e1, Question e2) { return e1.r < e2.r; }
int vP[maxN], cnt; //存质数
vector<int> its[maxN];  //某个数,它旗下的质因子
vector<int> RR[maxN];   //以i为右节点的数们
int prime[maxN];   //是否是质数:true质数、false非质数
void get_have(int index, int v)
{
    int i = 1;
    while(i<=cnt && v>1)
    {
        if(v%vP[i] == 0)
        {
            its[index].push_back(vP[i]);
            while(v%vP[i] == 0) v/=vP[i];
        }
        i++;
    }
}
void init()
{
    memset(prime, 0, sizeof(prime));
    cnt = 0;
    prime[0] = prime[1] = 1;
    for(int i=2; i<maxN; i++)
    {
        if(prime[i] == 0)
        {
            for(int j=2*i; j<maxN; j+=i) prime[j] = 1;
            vP[++cnt] = i;
        }
    }
    for(int i=0; i<maxN; i++) its[i].clear();
    for(int i=2; i<maxN; i++) get_have(i, i);
}
void init2()
{
    memset(l, 0, sizeof(l));
    memset(flag, 0, sizeof(flag));
    for(int i=0; i<=N+1; i++) RR[i].clear();
    for(int i=1; i<=N; i++)
    {
        int minn = 0;
        int len = (int)its[w[i]].size();
        for(int j=0; j<len; j++)
        {
            minn = max(minn, flag[its[w[i]][j]]);
            flag[its[w[i]][j]] = i;
        }
        l[i] = minn;
    }
    for(int i=1; i<=cnt; i++) flag[vP[i]] = N+1;
    for(int i=N; i>=1; i--)
    {
        int maxx = N + 1;
        int len = (int)its[w[i]].size();
        for(int j=0; j<len; j++)
        {
            maxx = min(maxx, flag[its[w[i]][j]]);
            flag[its[w[i]][j]] = i;
        }
        r[i] = maxx;
        RR[r[i]].push_back(i);
    }
}
int trie[maxN];
void update(int x, int val)
{
    if(x == 0) return;
    while(x <= N)
    {
        trie[x] += val;
        x += lowbit(x);
    }
}
int query(int x)
{
    int ans = 0;
    while(x)
    {
        ans += trie[x];
        x -= lowbit(x);
    }
    return ans;
}
int main()
{
    init();
    while(scanf("%d%d", &N, &Q) && (N | Q))
    {
        for(int i=1; i<=N; i++) scanf("%d", &w[i]);
        init2();
        for(int i=1; i<=Q; i++) { scanf("%d%d", &ques[i].l, &ques[i].r); ques[i].id = i; }
        sort(ques+1, ques+1+Q, cmp);
        memset(trie, 0, sizeof(trie));
        int i = 1;
        for(int q=1; q<=Q; q++)
        {
            while(i<=ques[q].r)
            {
                update(l[i], 1);
                int len = (int)RR[i].size();
                for(int j=0; j<len; j++)
                {
                    update(l[RR[i][j]], -1);
                    update(RR[i][j], 1);
                }
                i++;
            }
            int tmp = query(ques[q].r) - query(ques[q].l - 1);
            ans[ques[q].id] = ques[q].r - ques[q].l + 1 - tmp;
        }
        for(int i=1; i<=Q; i++) printf("%d\n", ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/84752445
今日推荐