zoukankan      html  css  js  c++  java
  • CF #365 703D. Mishka and Interesting sum

    题目描述

    D. Mishka and Interesting sum的意思就是给出一个数组,以及若干询问,每次询问某个区间[L, R]之间所有出现过偶数次的数字的异或和。
    这个东西乍看很像是经典问题,一列数字中所有数字出现偶数次,除了一个数字只出现一次,找出那个只出现过一次的数字。然而这个问题并不是要找出现奇数次数字的异或和。

    算法

    有一个直观的思路是求出[L, R的异或和,再异或上[L, R]之间所有出现过的数字的异或和。这样的话就可以使得数字出现次数的奇偶性发生变化。
    那么怎么求出区间[L, R]所有出现过的数字的异或和呢?
    先想一下如果是求区间[L, R]有多少种不同的数字怎么做?这个可以在遍历的时候保存每个数字最后出现的位置,打标记(去掉上一次的标记,更新到当前位置),去数[L, R]有多少个标记就行了。
    所以求[L, R]不同数字的异或和也是类似的做法,去掉之前出现位置的标记,给当前位置打上标记,去计算[L,R]所有标记异或和。
    回到原问题,有了区间不同数字的异或和,再异或上区间本身的异或值,就可以实现奇偶性翻转来得出出现偶数次的数字的异或和。

    具体的流程:
    需要一个异或BIT/FenwickTree,以及一个hashmap来保存元素出现位置。
    按照所有询问的right值排序,遍历所有数字,每一次遍历到当前要处理的询问的R值为止。
    每一步都要先往BIT中i位置加一个a[i](要做区间本身数字的异或)。
    如果当前数字是第一次出现,则向BIT中i位置加一个a[i],hashmap中标记a[i]出现的位置为i。
    如果当前数字之前已经出现过了,先去除上一次的标记,即向BIT中last[a[i]]加一个a[i](对于异或来说就抵消了),再向当前位置i加入一个a[i],更新hashmap中a[i]的位置为i。
    可以发现,无论数字是否是第一次出现,最终hashmap中都得要给i这个位置加一个a[i],和上述每一个位置都要插入a[i]正好就抵消效果了。

    所以流程可以简化为:每一步判断hashmap中是否存在a[i],存在的话取出位置值,BIT中往这个位置加a[i]。 在hashmap中插入<a[i], i>。
    每一个询问的答案就是bit.query(R)^bit.query(L-1)

    实现

    public class Main {
        public static void main(String[] args) {
            InputStream inputStream = System.in;
            OutputStream outputStream = System.out;
            InputReader in = new InputReader(inputStream);
            PrintWriter out = new PrintWriter(outputStream);
            TaskD solver = new TaskD();
            solver.solve(1, in, out);
            out.close();
        }
    
        static class TaskD {
            public void solve(int testNumber, InputReader in, PrintWriter out) {
                int n = in.nextInt();
                long[] a = new long[n + 1];
                for (int i = 1; i <= n; i++) {
                    a[i] = in.nextLong();
                }
                int m = in.nextInt();
                Query[] queries = new Query[m];
                for (int i = 0; i < m; i++) {
                    queries[i] = new Query(i, in.nextInt(), in.nextInt());
                }
                Arrays.sort(queries, (o1, o2) -> Integer.compare(o1.right, o2.right));
                Map<Long, Integer> pos = new HashMap<>();
                FenwickTree tree = new FenwickTree(n) {
                    @Override
                    protected long operate(long data, long value) {
                        return data ^ value;
                    }
                    @Override
                    public long sum(int s, int t) {
                        return sum(t) ^ sum(s - 1);
                    }
                };
                int i = 1;
                long[] result = new long[m];
                for (Query query : queries) {
                    for (; i <= query.right; i++) {
                        if (pos.containsKey(a[i])) {
                            tree.add(pos.get(a[i]), a[i]);
                        }
                        pos.put(a[i], i);
                    }
                    result[query.id] = tree.sum(query.left, query.right);
                }
                for (long ret : result) {
                    out.println(ret);
                }
            }
    
            class Query {
                int id;
                int left;
                int right;
    
                public Query(int id, int left, int right) {
                    this.id = id;
                    this.left = left;
                    this.right = right;
                }
    
            }
    
        }
    
        static class InputReader {
            private final BufferedReader reader;
            private StringTokenizer tokenizer;
    
            public InputReader(InputStream stream) {
                reader = new BufferedReader(new InputStreamReader(stream));
                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 int nextInt() {
                return Integer.parseInt(next());
            }
    
            public long nextLong() {
                return Long.parseLong(next());
            }
    
        }
    
        static class FenwickTree {
            private final long[] data;
    
            public FenwickTree(int size) {
                data = new long[size + 1];
            }
    
            public FenwickTree(long[] data) {
                this.data = data;
            }
    
            private int lowBit(int x) {
                return x & -x;
            }
    
            protected long operate(long data, long value) {
                return data + value;
            }
    
            public final void add(int p, long v) {
                for (int i = p; i < data.length; i += lowBit(i)) {
                    data[i] = operate(data[i], v);
                }
            }
    
            public final long sum(int p) {
                long ret = 0;
                for (int i = p; i > 0; i -= lowBit(i)) {
                    ret = operate(ret, data[i]);
                }
                return ret;
            }
    
            public long sum(int s, int t) {
                return sum(t) - sum(s - 1);
            }
    
        }
    }
    
  • 相关阅读:
    判断字符中是否包含汉字
    since I lived here; since I have lived here. 的区别? 从语法上看, 为啥会有这样的区别?
    have married; have been married; 到底是结婚了没?还是已经离婚了?
    C#项目依据 x86 x64 配置不同的引用
    现在完成时可以表示过去事件对现在的影响/效果. 过去完成时也可以起相同的作用!!!!
    使用现在完成时的常见错误(转发)
    去除win10下的缺省ctrl加空格功能
    appear + 表语 与 appear to be + "表语" 的区别; get hurt与 get to be hurt的区别
    ssm搭建的一个web应用的工作流程
    return和finally究竟谁先执行,还有return是怎么返回数据的
  • 原文地址:https://www.cnblogs.com/micrari/p/5745051.html
Copyright © 2011-2022 走看看