[SDOI2009]HH的项链 --- 题解 (离线操作 + 树状数组)

目录

[SDOI2009]HH的项链:

        题目大意: 

思路解析:

代码实现:

        


 

 

[SDOI2009]HH的项链:

        题目大意: 

思路解析:

        这道题的难点是,如果 l1 -- r1 这个区间有四个不同的贝壳, l2 -- r2 这个区间有3个不同的贝壳,我们并不能得到 l1 -- r2 这个区间有7个不同的贝壳,因为可能这两个区间有相同的贝壳。

        但是如果我们把相同的贝壳让最后出现的这个贝壳贡献1的答案,其余相同贝壳不贡献答案,那么可能会引出  其他问题。例如:

如果考虑矩形这个区间答案为1,但实际为2。但可以发现如果我们固定右端点, 只修改右端点以前的答案,那么这个区间的任何查询都是对的。例如:

可以发现在 1 -4, 2-4, 3-4这样的区间下一定是对的,那么这里就可以引出离线操作,即我们先记录有哪些查询的右端点属于 ai这个端点,这样枚举右端点,并对右端点之前的答案进行修改后,修改完成后,使用树状数组区间查询答案即可。

代码实现:

        

import java.io.*;
import java.util.*;


public class Main {
    static long mod = 998244353;
    static int MAXN = 200005;
    static int[] a = new int[MAXN];
    static int[] t = new int[MAXN];
    static Vector<Node>[] g;



    public static void main(String[] args) throws IOException {
        int n = input.nextInt();
        g = new Vector[n+1];
        int[] b = new int[n+1];
        for (int i = 1; i <= n; i++) {
            b[i] = input.nextInt();
            g[i] = new Vector<>();
        }
        int q = input.nextInt();
        for (int i = 0; i < q; i++) {
            int l = input.nextInt();
            int r = input.nextInt();
            g[r].add(new Node(i, l));
        }
        int[] ans = new int[q];
        int[] pre = new int[1000005];
        for (int i = 1; i <= n; i++) {
            if (pre[b[i]] != 0){
                update(pre[b[i]], -1, n);
                a[pre[b[i]]]--;
                pre[b[i]] = i;
                update(i,1,n);
                a[i]++;
            }else {
                update(i,1,n);
                a[i]++;
                pre[b[i]] = i;
            }
            for (int j = 0; j < g[i].size(); j++) {
                Node cur = g[i].get(j);
                ans[cur.id] = sum(cur.l, i, n);
            }
        }
        for (int i = 0; i < q; i++) {
            out.println(ans[i]);
        }
        out.flush();
        out.close();
        br.close();
    }
    public static int lowbit(int x){
        return x & (-x);
    }
    public static void update(int x, int val, int n){
        for (int i = x; i <= n; i+=lowbit(i)) {
            t[i] += val;
        }
    }
    public static int sum(int l, int r, int n){
        int sum = 0;
        while (l <= r){
            sum += a[r];
            r--;
            while (r - lowbit(r) >= l){
                sum += t[r];
                r -= lowbit(r);
            }
        }
        return sum;
    }
    public static class Node{
        int id;
        int l;
        public Node(int id, int l){
            this.id = id;
            this.l = l;
        }
    }
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static Input input = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public char[] nextChars(){return next().toCharArray();}
        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }
    }
}

         

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值