zoukankan      html  css  js  c++  java
  • LG 题解 P4587 [FJOI2016]神秘数

    题目传送

    思路挺仙的一题,不过有了思路就是个主席树的板子了

    前置芝士

    • 主席树

    Description

    一个可重复数字集合 (S) 的神秘数定义为最小的不能被 (S) 的子集的和表示的正整数。
    现在给你一个长度为 (n) 的序列 (a) ,每次询问一个 ([l,r]),问 ([l,r]) 区间的数组成的集合的神秘数。
    (n,m le 10^5, sum a_i le 10^9)

    Solution

    考虑没有区间查询时怎么做。

    首先肯定要考虑有没有 (1) ,没有直接输出 (1)
    先对 (a) 序列进行排序。
    设当前我们已经表示出了 ([1,x]),下一个添加进去的数为 (a_i)

    • 如果 (a_i le x+1),填加后能表示出来的区间为 ([1,x] cup [1 + a_i, x + a_i] = [1, x + a_i])
    • 如果 (a_i > x + 1),那么 (x+1) 就表示不出来,所以答案为 (x+1)

    这样一次询问的复杂度是 (O(n)) 的。

    考虑优化这个添加过程。

    假设用值域在 ([1,lst]) 区间内的数可以表示出来的区间为 ([1,now])
    那么我们再把值域在 ([1,now+1]) 区间内的任何数放进去都是合法的。
    因为我们已经把值域在 ([1,lst]) 内的所有数放进去了,那么我们设 (sum = sum a_i [lst + 1 le a_i le now + 1]),即对值域在 ([lst+1, now+1]) 内的数求和,把他们一起放进去。
    那么我们已经用过的数的区间为 ([1,now + 1]),可以表示出来的数的区间为 ([1,now+sum])
    注:上面这块的区间均指值域区间而不是下标区间,注意区分。

    上面这个值域的扩展过程类似于斐波那契数列,所以说复杂度是 (log (sum a_i)) 的。

    查询一段区间内一个值域区间的和就是主席树板子做的事了。单次求和为 $log $ 复杂度。

    所以总复杂度为 (O(n log n log (sum a_i)))

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<string>
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    const int Max = 1e9;
    
    int n, m;
    int root[MAXN];
    
    int read(){
    	int s = 0, f = 0;
    	char ch = getchar();
    	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    	return f ? -s : s;
    }
    
    namespace Hjt {
        #define ls lson[now_]
        #define rs rson[now_]
        int lson[MAXN << 5], rson[MAXN << 5], val[MAXN << 5], node_num = 0;
        void Insert(int &now_, int pre_, int l, int r, int pos, int val_) {
            now_ = ++ node_num;
            val[now_] = val[pre_] + val_; 
            ls = lson[pre_], rs = rson[pre_];
            if(l == r) return ;
            int mid = (l + r) >> 1;
            if(pos <= mid) Insert(ls, lson[pre_], l, mid, pos, val_);
            else Insert(rs, rson[pre_], mid + 1, r, pos, val_);
        }
        int Query(int now_, int pre_, int l, int r, int L, int R) {
            if(!(val[now_] - val[pre_])) return 0;
            if(L <= l && r <= R) return val[now_] - val[pre_];
            int mid = (l + r) >> 1, res = 0;
            if(mid >= L) res += Query(ls, lson[pre_], l, mid, L, R);
            if(mid < R) res += Query(rs, rson[pre_], mid + 1, r, L, R);
            return res;
        }
    }
    
    signed main()
    {
        n = read();
        for(int i = 1, x; i <= n; ++i) {
            x = read();
            Hjt::Insert(root[i], root[i - 1], 1, Max, x, x);
        }
        m = read();
        for(int i = 1, l, r; i <= m; ++i) {
           l = read(), r = read();
           int lst = 0, now = 0;
           while(1 + 2 == 3) {
               int tmp = Hjt::Query(root[r], root[l - 1], 1, Max, lst + 1, now + 1);
    //           cout<<"tmp: "<<tmp<<"
    ";
               if(tmp) lst = now + 1, now += tmp;
               else break;
           }
           printf("%lld
    ", now + 1);
        }
    	return 0;
    }
    
  • 相关阅读:
    Docker(二十一)-Docker Swarm集群部署
    Docker(十八)-Docker配置DNS
    Docker(十七)-修改Docker容器启动配置参数
    Docker(十六)-Docker的daemon.json的作用
    JavaScript实现无缝滚动 原理详细讲解
    JS 数字 、中文、 英文、判断
    JS常用属性方法大全
    vue.js插件使用(01) vue-resource
    Vue.js常见问题
    web前端开发必备技术
  • 原文地址:https://www.cnblogs.com/Silymtics/p/solution-P4587.html
Copyright © 2011-2022 走看看