• 【Codechef FRBSUM】【FJOI2016】【BZOJ4299】【BZOJ 4408】 可持久化线段树


    4408: [Fjoi 2016]神秘数

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 475  Solved: 287
    [Submit][Status][Discuss]

    Description

    一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数。例如S={1,1,1,4,13},

    1 = 1

    2 = 1+1

    3 = 1+1+1

    4 = 4

    5 = 4+1

    6 = 4+1+1

    7 = 4+1+1+1

    8无法表示为集合S的子集的和,故集合S的神秘数为8。

    现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由a[l],a[l+1],…,a[r]所构成的可重复数字集合的神秘数。

    Input

    第一行一个整数n,表示数字个数。
    第二行n个整数,从1编号。
    第三行一个整数m,表示询问个数。
    以下m行,每行一对整数l,r,表示一个询问。

    Output

    对于每个询问,输出一行对应的答案。

    Sample Input

    5
    1 2 4 9 10
    5
    1 1
    1 2
    1 3
    1 4
    1 5

    Sample Output

    2
    4
    8
    8
    8

    HINT

    对于100%的数据点,n,m <= 100000,∑a[i] <= 10^9

    Source

    鸣谢yyh上传

    Solution

    这题的思路还是很妙的。

    我们假设前面选的数能取到的最大值为k,即[1,k]中的所有数都能取到,现在加入了一个数x。

    若x <= k+1,则能取到的最大值为k+x,神秘数为k+x+1

    若x > k+1,则能取到的最大值仍为k,神秘数为k+1

    对于一个询问(l,r),设当前神秘数为k,若Σai >= k(ai <= k && i∈[l,r]),则说明k仍能增大,否则,神秘数即为k。

    而求前缀和,可以用可持久化线段树来解决。

    每次寻找,k至少增加一倍,每次求前缀和是logn,总的时间复杂度是O(Q*log1e92)

    Code

     1 #include <cstdio>
     2 #include <cstdlib>
     3 #include <cstring>
     4 #include <string>
     5 #include <algorithm>
     6 
     7 using namespace std;
     8 
     9 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
    10 typedef long long LL;
    11 const int maxn = 1e5+10;
    12 int n, a[maxn];
    13 struct Node
    14 {
    15     LL sum; int ls, rs;
    16     Node() { sum = ls = rs = 0; }
    17 }t[maxn*50];
    18 int rt[maxn], t_cnt, mx_a;
    19 
    20 void pushup(Node &x) { x.sum = t[x.ls].sum+t[x.rs].sum; }
    21 
    22 void update(int now_rt, int las_rt, int l, int r, int x)
    23 {
    24     if (l == r)
    25     {
    26         t[now_rt].sum = t[las_rt].sum+x;
    27         return ;
    28     }
    29     int mid = (l+r)>>1;
    30     if (x <= mid)
    31     {
    32         t[now_rt].rs = t[las_rt].rs, t[now_rt].ls = ++t_cnt;
    33         update(t[now_rt].ls, t[las_rt].ls, l, mid, x);
    34     }
    35     else
    36     {
    37         t[now_rt].ls = t[las_rt].ls, t[now_rt].rs = ++t_cnt;
    38         update(t[now_rt].rs, t[las_rt].rs, mid+1, r, x);
    39     }
    40     pushup(t[now_rt]);
    41 }
    42 
    43 int query(int rt_1, int rt_2, int l, int r, int x)
    44 {
    45     if (l > x) return 0;
    46     if (r <= x) return t[rt_2].sum-t[rt_1].sum;
    47     int mid = (l+r)>>1;
    48     int ret = query(t[rt_1].ls, t[rt_2].ls, l, mid, x);
    49     if (x > mid) ret += query(t[rt_1].rs, t[rt_2].rs, mid+1, r, x);
    50     return ret;
    51 }
    52 
    53 int calc(int l, int r)
    54 {
    55     int ret = 1, temp = 0;
    56     while ((temp = query(rt[l-1], rt[r], 1, mx_a, ret)) >= ret) ret = temp+1;
    57     return ret;
    58 }
    59 
    60 int main()
    61 {
    62     scanf("%d", &n);
    63     REP(i, 1, n) scanf("%d", &a[i]), mx_a = max(mx_a, a[i]);
    64     REP(i, 1, n) update(rt[i] = ++t_cnt, rt[i-1], 1, mx_a, a[i]);
    65     int Q, l, r;
    66     scanf("%d", &Q);
    67     while (Q --)
    68     {
    69         scanf("%d %d", &l, &r);
    70         printf("%d
    ", calc(l, r));
    71     }
    72     return 0;
    73 }
    View Code
  • 相关阅读:
    修辞方法文章结构
    小学弟的随机字母
    kindeditor在JSP中的使用
    没有规矩,不成方圆
    在博客园安家啦
    Windows下,mysqlcc的安装及配置(Config MySQL Control Center)
    Windows下,MySql的安装及配置(Install and Config of MySQL in Windows)
    软件使用说明书
    Windows 8 Customer Preview使用心得
    How to Choose a secure password,Ubuntu tell you!
  • 原文地址:https://www.cnblogs.com/-ZZB-/p/6732388.html
Copyright © 2020-2023  润新知