[SDOI2009] 洛谷P1972 HH的项链-------离线方法 && 在线主席树

P1972 [SDOI2009]HH的项链
题目背景


题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入输出格式
输入格式:

第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:

M 行,每行一个整数,依次表示询问对应的答案。

输入输出样例
输入样例#1:

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

输出样例#1:

2
2
4

说明

数据范围:

对于100%的数据,N <= 50000,M <= 200000。

下面是我要介绍的两种做法:

<1>离线方法:
首先, 我们可以现将贝壳编号离散化, 缩小数据范围(0 ~ 1000000) -> (0 ~ 50000)
然后, 观察询问, 发现并不强制在线, 那就先把所有询问记录下来(q), 顺便编个号(id), 然后按照左端点(l)升序排序, 若相同, 就按右端点(r)排序, (详见cmp2()函数), 接下来就可以一个个地解决问题啦(solve())
定义:
l, r : 现在统计到的区间[l, r]
now: 现在解决到第几个问题
sum: 现在区间中有多少个不同的贝壳
对于每一个问题, 我们都一个个地把l, r 移动到相应位置, 顺便统计答案, 然后将答案存下来(ans[]), 最后按顺序输出就行啦!

代码:

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>

#define maxn 50005
#define For(a, b, c) for(a = b; a <= c; ++a)
using namespace std;
int n, m, id[maxn], ans[200010];
int p[maxn], cnt;
struct B{
    int c, id;
}a[maxn];
struct que{
    int l, r, id;
}q[200010];

int read(){
    char x;
    while((x = getchar()) > '9' || x < '0') ;
    int u = x - '0';
    while((x = getchar()) <= '9' && x >= '0') u = (u << 3) + (u << 1) + x - '0';
    return u;
}

bool cmp1(B a, B b){ return a.c < b.c; }

bool cmp2(que a, que b){
    if(a.l == b.l) return a.r < b.r;
    return a.l < b.l;
}

void pre(){
    int i;
    sort(q + 1, q + m + 1, cmp2);
    sort(a + 1, a + n + 1, cmp1);//离散化
    For(i, 1, n) id[a[i].id] = (a[i].c == a[i - 1].c) ? cnt : ++cnt;
    //编号相同应属于同一类
}

void solve(){
    int l = 1, r = 1, sum = 1, now = 1;
    ++p[id[1]];
    while(now <= m){
        while(r < q[now].r) if(++p[id[++r]] == 1) ++sum;
        while(l < q[now].l) if(!--p[id[l++]]) --sum;
        while(r > q[now].r) if(!--p[id[r--]]) --sum;//移动l, r,记录
        ans[q[now++].id] = sum;//存入答案
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("L.in", "r", stdin);
    freopen("L.out","w",stdout);
#endif
    int i;
    n = read();
    For(i, 1, n) a[i].c = read(), a[i].id = i;
    m = read();
    For(i, 1, m) q[i].l = read(), q[i].r = read(), q[i].id = i;
    pre();
    solve();
    For(i, 1, m) printf("%d\n", ans[i]);
    return 0;
}

<2>在线主席树
对于每一个贝壳, 记录它后面最近一个与它相同的贝壳的位置(ne[i]),若没有则记为 n + 1
那么, 对于每一个询问[l, r],我们就是要求出所有的 i (i >= l && i <= r && ne[i] > r) 的个数
于是, 就可以主席树做了

代码:

#include<iostream>
#include<cstdlib>
#include<cstdio>

#define L(x) ch[x][0]
#define R(x) ch[x][1]
#define maxn 50005
#define maxm 1000001
#define For(a, b, c) for(a = b; a <= c; ++a)
#define Rep(a, b, c) for(a = b; a >= c; --a)
using namespace std;
int n, m, a[maxn], pro[1000005], ne[maxn];

int read(){
    char x;
    while((x = getchar()) > '9' || x < '0') ;
    int u = x - '0';
    while((x = getchar()) <= '9' && x >= '0') u = (u << 3) + (u << 1) + x - '0';
    return u;
}

inline void pre(){
    int i;
    Rep(i, n, 1){
        ne[i] = pro[a[i]] ? pro[a[i]] : n + 1;
        pro[a[i]] = i;
    }
}

struct Chairman_Tree{
    int e, rt[maxn], ch[maxm][2], w[maxm];
    void cpy(int x, int y){ ch[x][0] = ch[y][0], ch[x][1] = ch[y][1], w[x] = w[y]; }
    void insert(int &q, int x, int l = 1, int r = n + 1){
        cpy(++e, q);
        ++w[q = e];
        if(l == r) return ;
        int mid = (l + r) >> 1;
        if(x <= mid) insert(L(q), x, l, mid);
        else insert(R(q), x, mid + 1, r);
    }
    int query(int t1, int t2, int num, int l = 1, int r = n + 1){
        if(l == r) return num > l ? w[t2] - w[t1] : 0;
        int mid = (l + r) >> 1, k = w[L(t2)] - w[L(t1)];
        if(num <= mid) return query(L(t1), L(t2), num, l, mid) + w[R(t2)] - w[R(t1)];
        else return query(R(t1), R(t2), num, mid + 1, r); 
    }
}d;

void build(){
    int i;
    pre();
    For(i, 1, n) d.rt[i] = d.rt[i - 1], d.insert(d.rt[i], ne[i]);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("L.in", "r", stdin);
    freopen("L.out","w",stdout);
#endif
    int i;
    n = read();
    For(i, 1, n) a[i] = read();
    build();
    m = read();
    For(i, 1, m){
        int l = read(), r = read();
        printf("%d\n", d.query(d.rt[l - 1], d.rt[r], r));
    }
    return 0;
}


----------

然后这道题就完了, 离线还可以用莫队算法优化一下, 有兴趣的可以到:
[莫队算法——解决序列上询问的利器 ](http://blog.csdn.net/wt_cnyali/article/details/54897102)
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值