【NOIP2016提高A组8.11】自然数

题目

分析

\(O(n)\)求出mex(1,i)(1<=i<=n):

虽然0<=ai<=10^9,但只有n个数,所以mex一定小于等于n
for(long long j=1;j<=n;j++)
{
        if(a[j]<=n)
            bz[a[j]]=false;
        for(long long k=top;k<=n;k++)
        {
            if(bz[k])
            {
                top=k;
                ans+=top;
                break;
            }
        }
}

显然mex是单调不下降的,
接着用线段树维护mex。
如果删掉a[i],从下一个mex比a[i]大的位置到下一个a[i]的位置之前的mex都会改变,都会变成a[i]。
所以用线段树维护区间最大mex以及区间mex和。

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483647;
const long long mo=1000000007;
const long long N=2010000;
using namespace std;
long long a[N],n,ans=0,next[N];
long long bz[N]={0};
struct ddx
{
    long long sum,mxe,lazy;
}tree[N*4];
long long put(long long v,long long l,long long r,long long x,long long y)
{
    if(l==r)
    {
        tree[v].mxe=tree[v].sum=y;
        return 0;
    }
    long long mid=(l+r)/2;
    if(x<=mid)
        put(v*2,l,mid,x,y);
    else
        put(v*2+1,mid+1,r,x,y);
    tree[v].sum=tree[v*2].sum+tree[v*2+1].sum;
    tree[v].mxe=max(tree[v*2].mxe,tree[v*2+1].mxe);
}
long long findpos(long long v,long long l,long long r,long long x)
{
    if(l==r)
    {
        if(tree[v].mxe>x)
            return l;
                else return maxlongint;
    }
    long long mid=(l+r)/2;
    if(tree[v].lazy>=0)
    {
        tree[v*2].mxe=tree[v*2+1].mxe=tree[v*2].lazy=tree[v*2+1].lazy=tree[v].lazy;
        tree[v*2].sum=tree[v].lazy*(mid-l+1);
        tree[v*2+1].sum=tree[v].lazy*(r-mid);
        tree[v].lazy=-1;
    }
    long long o=0;
    if(tree[v*2].mxe>x)
        o=findpos(v*2,l,mid,x);
    else
        o=findpos(v*2+1,mid+1,r,x);
    tree[v].sum=tree[v*2].sum+tree[v*2+1].sum;
    tree[v].mxe=max(tree[v*2].mxe,tree[v*2+1].mxe);
    return o;
}
long long change(long long v,long long l,long long r,long long x,long long y,long long z)
{
    if(l==x && y==r)
    {
        tree[v].sum=z*(r-l+1);
        tree[v].mxe=z;
        tree[v].lazy=z;
        return 0;
    }
    long long mid=(l+r)/2;
    if(tree[v].lazy>=0)
    {
        tree[v*2].mxe=tree[v*2+1].mxe=tree[v*2].lazy=tree[v*2+1].lazy=tree[v].lazy;
        tree[v*2].sum=tree[v].lazy*(mid-l+1);
        tree[v*2+1].sum=tree[v].lazy*(r-mid);
        tree[v].lazy=-1;
    }
    if(y<=mid)
        change(v*2,l,mid,x,y,z);
    else
    if(x>mid)
        change(v*2+1,mid+1,r,x,y,z);
    else
    {
        change(v*2,l,mid,x,mid,z);
        change(v*2+1,mid+1,r,mid+1,y,z);
    }
    tree[v].sum=tree[v*2].sum+tree[v*2+1].sum;
    tree[v].mxe=max(tree[v*2].mxe,tree[v*2+1].mxe);
}
long long find(long long v,long long l,long long r,long long x,long long y)
{
    if(l==x && y==r)
    {
        ans+=tree[v].sum;
        return 0;
    }
    long long mid=(l+r)/2;
    if(tree[v].lazy>=0)
    {
        tree[v*2].mxe=tree[v*2+1].mxe=tree[v*2].lazy=tree[v*2+1].lazy=tree[v].lazy;
        tree[v*2].sum=tree[v].lazy*(mid-l+1);
        tree[v*2+1].sum=tree[v].lazy*(r-mid);
        tree[v].lazy=-1;
    }
    if(y<=mid)
        find(v*2,l,mid,x,y);
    else
    if(x>mid)
        find(v*2+1,mid+1,r,x,y);
    else
    {
        find(v*2,l,mid,x,mid);
        find(v*2+1,mid+1,r,mid+1,y);
    }
    tree[v].sum=tree[v*2].sum+tree[v*2+1].sum;
    tree[v].mxe=max(tree[v*2].mxe,tree[v*2+1].mxe);
}
int main()
{
    scanf("%lld",&n);
    for(long long i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        if(a[i]<=n)
        {
            next[bz[a[i]]]=i;
            bz[a[i]]=i;
        }
    }
    for(long long i=1;i<=n;i++)
        if(!next[i])
            next[i]=n+1;
    memset(bz,true,sizeof(bz));
    for(long long i=0;i<=N*4-1;i++)
        tree[i].lazy=-1;
    long long top=0;
    ans=0;
    for(long long j=1;j<=n;j++)
    {
            if(a[j]<=n)
                bz[a[j]]=false;
            for(long long k=top;k<=n;k++)
            {
                if(bz[k])
                {
                    top=k;
                    ans+=top;
                    break;
                }
            }
        put(1,1,n,j,top);
    }
    for(long long i=2;i<=n;i++)
    {
        if(a[i-1]<=n)
        {
            long long pos=findpos(1,1,n,a[i-1]);
            if(next[i-1]-1>=pos)
                change(1,1,n,pos,next[i-1]-1,a[i-1]);
        }
        find(1,1,n,i,n);
    }
    printf("%lld",ans);
}

原文地址:https://www.cnblogs.com/chen1352/p/9043463.html

时间: 2024-08-30 17:54:18

【NOIP2016提高A组8.11】自然数的相关文章

【NOIP2016提高A组集训第4场11.1】平衡的子集

题目 夏令营有N个人,每个人的力气为M(i).请大家从这N个人中选出若干人,如果这些人可以分成两组且两组力气之和完全相等,则称为一个合法的选法,问有多少种合法的选法? 分析 如果暴力枚举每个人被分到哪个组或不分,O(2^20)显然会超时. 我们换一种思路, 每次只枚举一半, 将前后半部分分开枚举后半部分,枚举出每种的和以及有没有被选的状态. 枚举和相同的前后部分,如果这种状态没有被选过,就ans+1,然后将这种状态打个标记,这种状态就不再产生贡献. #include <cmath> #incl

【NOIP2016提高A组模拟8.17】(雅礼联考day1)Binary

题目 分析 首先每个数对\(2^i\)取模.也就是把每个数的第i位以后删去. 把它们放进树状数组里面. 那么当查询操作, 答案就位于区间\([2^i-x,2^{i-1}-1-x]\)中,直接查询就可以了. 细节很多,注意处理. #include <cmath> #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algor

【NOIP2016提高A组五校联考4】square

题目 分析 首先,设\(f_{i,j}\)表示最大的以(i,j)为左下角的正方形的边长. 转移显然,\(f_{i,j}=\max(f_{i-1,j},f_{i,j-1},f_{i-1,j-1})+1\) 接着,再设\(g_{i,j,k,l}\)表示在以\((k,l)\)为左上角,\((k+2^i-1,l+2^j-1)\)为右下角的矩阵中,最大的f. 二维rmq就不讲了. 假设询问矩阵(x,y,x1,y1), 二分答案ans(想想为什么?) 用rmq看红色区域中的最大f值是否合法. 注意:出题人将

【NOIP2016提高A组模拟10.15】算循环

题目 分析 一步步删掉循环, 首先,原式是\[\sum_{i=1}^n\sum_{j=1}^m\sum_{k=i}^n\sum_{l=j}^m\sum_{p=i}^k\sum_{q=j}^l1\] 删掉最后两个循环 \[\sum_{i=1}^n\sum_{j=1}^m\sum_{k=i}^n\sum_{l=j}^m(k-i+1)(l-j+1)\] 发现,当\(i,j\)固定,随着\(k,l\)的变化,\((k-i+1),(l-j+1)\)都是每次减少1 SO, \[\sum_{i=1}^n\su

【NOIP2016提高A组模拟8.15】Password

题目 分析 首先我们知道,原A序列其实表示一个矩阵,而这个矩阵的对角线上的数字就是答案B序列. 接着\(a.b>=gcd(a,b)\),所以序列A中的最大的数就是ans[1],第二大的数就是ans[2]. 但是ans[3]并不一定就是序列A中的第三大的数,因为gcd(ans[1],ans[2])有可能是序列A中的第三大的数. 所以但找到了ans[i],对于每个gcd(ans[i],ans[1~i-1])在序列A中删掉两个(就是删掉2(i-1)个.为什么是两个自己考虑).时间复杂度\(O(n^2l

【NOIP2016提高A组模拟8.14】传送带

题目 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.FTD在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在FTD想从A点走到D点,他想知道最少需要走多长时间 分析 易得,答案就是首先在AB上走一段,然后走到CD上的一点,再走到D. 正解就是三分套三分,但本人很懒,打了个枚举加三分,勉强卡了过去. 首先在AB上枚举一点,接着在CD上按时间三分. #include <cmath> #include <iostrea

【NOIP2016提高A组8.12】通讯

题目 "这一切都是命运石之门的选择." 试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短信,并由此得知了伦太郎制作出了电话微波炉(仮). 为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯网络,传达到所有分部. SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线路有一个固定的通讯花费Ci. 为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向另一个与它有线路的部门传递(可能存在多条通信线路).我们定义总费用为所

【NOIP2016提高A组8.12】奇袭

题目 由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上要迎来最终的压力测试--魔界入侵. 唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量是远远不够的.所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族. 在UW的驻地可以隐约看见魔族军队的大本营.整合骑士们打算在魔族入侵前发动一次奇袭,袭击魔族大本营! 为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族大本营进行侦查,并计算出袭击的难度. 经过侦查,你绘制出了魔族大

【NOIP2016提高A组8.12】总结

惨败!!!! 第一题是一道神奇的期望问题. 第二题,发现"如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费"这个条件,就想到要用tarjan缩点,不过打完tarjan之和就没有思路了,爆零.后来才知道只用比较大小就OK了. 第三题,没有思路只打了个暴力,30分. 接着强烈谴责出题人 怎么可以那么马虎. 原文地址:https://www.cnblogs.com/chen1352/p/9043468.html