zoukankan      html  css  js  c++  java
  • bzoj 4408: [Fjoi 2016]神秘数 数学 可持久化线段树 主席树

    https://www.lydsy.com/JudgeOnline/problem.php?id=4299

    一个可重复数字集合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

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

    题解
    A : 
    将a从小到大排序,设当前神秘数为ans,扫到了a[i],那么
    1. a[i] < ans 时, ans = ans + a[i] ;
    2. a[i] > ans时, ans就是最小的神秘数, 跳出循环. 
    B : 
    我们也可以发现神秘数简化推法,ans初始为1,那么下一个ans为(sigma (a[i]<=ans) a[i])+1 . (用A部分方法压缩的想法来思考 [ lastans , nowans ) 区间内数的填充), 能够看出sigma的次数是log级的. 
    此时我们需要维护的是任意区间内的sigma, 主席树可以实现. 
    这里的主席树并没有离散化,因为主席树只需要建 n*( log总长 ) 个点, 这样写更方便 ( 常数变大了但是并不是很影响复杂度 ), 离散化也阔以, 不过注意一下查找的时候用upper_bound. 
     
    (在这里mark一下lower_bound和upper_bound的方向)
     
     
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 const int maxn=100010;
     8 int n,m,cnt=0,tot=0;
     9 int a[maxn]={},rt[maxn]={};
    10 int sum[maxn*60]={},lc[maxn*60]={},rc[maxn*60]={};
    11 int read(){
    12     int w=0,f=1;char ch=getchar();
    13     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    14     while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    15     return w*f;
    16 }
    17 void Build(int l,int r,int y,int &x,int v){
    18     x=++tot;sum[x]=sum[y]+v;
    19     if(l==r)return;
    20     lc[x]=lc[y];rc[x]=rc[y];
    21     int mid=(l+r)/2;
    22     if(v<=mid) Build(l,mid,lc[y],lc[x],v);
    23     else Build(mid+1,r,rc[y],rc[x],v);
    24 }
    25 int Query(int l,int r,int x,int y,int v){
    26     if(l==r)return sum[y]-sum[x];
    27     //cout<<sum[y]<<sum[x]<<l<<r<<endl;
    28     int mid=(l+r)/2;
    29     if(v<=mid) return Query(l,mid,lc[x],lc[y],v);
    30     else return Query(mid+1,r,rc[x],rc[y],v)+sum[lc[y]]-sum[lc[x]];
    31 }
    32 int main(){
    33     //freopen("a.in","r",stdin);
    34     //freopen("a.out","w",stdout);
    35     n=read();
    36     for(int i=1;i<=n;i++){a[i]=read();cnt+=a[i];}
    37     for(int i=1;i<=n;i++)Build(1,cnt,rt[i-1],rt[i],a[i]);
    38     m=read();
    39     for(int i=1;i<=m;i++){
    40         int l=read();int r=read();
    41         int ans=1;
    42         for(;;){
    43             int z=Query(1,cnt,rt[l-1],rt[r],ans);
    44             //cout<<z<<endl;
    45             if(z<ans)break;
    46             ans=z+1;
    47         }
    48         printf("%d
    ",ans);
    49     }
    50     return 0;
    51 }
    View Code

  • 相关阅读:
    《别让不会记笔记害了你》豆瓣:3.9
    《费曼学习法用输出倒逼输入》 豆瓣:6.6
    《面人麻生》豆瓣:8.8 &《哈瓦那》豆瓣:7.8
    C# 获取汉语拼音全码及简码
    SQL Server 获取汉语拼音简码
    ASP.NET 3.5中的ListView控件和DataPager控件
    Appliction_BeginRequest
    身份证验证
    取得数据库表信息的Sql语句
    Spark shuffle失败的可能原因 及解决办法
  • 原文地址:https://www.cnblogs.com/137shoebills/p/8747691.html
Copyright © 2011-2022 走看看