hdu4747 mex 线段树

题意:给一个序列不超过200000个元素,定义mex(i,j)是区间[i,j]之间所没有的最小非负整数。求sum(mex[i,j])对于所有1<=i<=j<=n;

解法:线段树。先求出mex(1,1),mex(1,2),mex(1,3)...mex(1,n) 而且这必然是递增的。

然后 sum[i=1,1<=j<=n]就算出来了,然后去掉arr[1],这时候会影响到的是下一个arr[1]出现前mex值大于arr[1]的那些位置,而且由于mex具有单调性,如果有必然是连续的一个区间,所以区间修改即可。修改完,讲arr[1]置0,求和就是sum[i=2,2<=j<=n],以此不断往后计算。就得到了sum(mex[1<=i<=n,i<=j<=n]);

代码:

/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=200010;
const LL INF=0x3FFFFFFF;
int arr[Max];
int mex[Max];
map<int,int> maps;
struct node
{
    LL sum;//表示和
    int ma;//表示区间最大值
    int lazy;//表示区间值一样
    int l,r;
    node* pl,*pr;
} tree[3*Max];
int tot=0;
int next[Max];
void build(node* p,int left,int right)
{
    p->l=left;
    p->r=right;
    if(left==right)
    {
        p->ma=mex[left];
        p->sum=mex[left];
        p->lazy=0;
        return ;
    }
    int middle=(left+right)/2;
    tot++;
    p->pl=tree+tot;
    build(p->pl,left,middle);
    tot++;
    p->pr=tree+tot;
    build(p->pr,middle+1,right);
    p->sum=p->pl->sum+p->pr->sum;
    p->ma=max(p->pl->ma,p->pr->ma);
}

void update(node* p,int value,int left,int right);
void down(node* p)
{
    if(p->l==p->r)
        return ;
    p->lazy=0;
    int middle=(p->l+p->r)/2;
    update(p->pl,p->ma,p->l,middle);
    update(p->pr,p->ma,middle+1,p->r);
}
int findpos(node* p,int value)
{
    if(p->l==p->r)
    {
        if(p->ma>=value)
            return p->l;
    }
    if(p->lazy)
        down(p);
    if(p->pl->ma>value)
        return findpos(p->pl,value);
    return findpos(p->pr,value);
}
void update(node* p,int value,int left,int right)
{
    if(p->l==left&&p->r==right)
    {
        p->lazy=1;
        p->ma=value;
        p->sum=(p->r-p->l+1)*value;
        return;
    }
    LL ans=0;
    int middle=(p->l+p->r)/2;
    if(p->lazy)
    {
        down(p);
    }
    if(left>middle)
    {
        update(p->pr,value,left,right);
    }
    else if(right<=middle)
        update(p->pl,value,left,right);
    else
    {
        update(p->pl,value,left,middle);
        update(p->pr,value,middle+1,right);
    }
    p->sum=p->pl->sum+p->pr->sum;
    p->ma=max(p->pl->ma,p->pr->ma);
}
int n;
int main()
{

    while(cin>>n&&n)
    {
        tot=0;
        memset(tree,0,sizeof tree);
        memset(next,0,sizeof next);
        maps.clear();
        for(int i=1; i<=n; i++)
            scanf("%d",arr+i);
        int p=0;
        for(int i=1; i<=n; i++)
        {
            next[maps[arr[i]]]=i;
            maps[arr[i]]=i;
            while(maps.find(p)!=maps.end())p++;
            mex[i]=p;
        }
        build(tree,1,n);
        LL ans=0;
        for(int i=1; i<=n; i++)
        {
            ans+=tree->sum;
            if(next[i]==0) next[i]=n+1;
            if(tree->ma>arr[i])
            {
                int pos=findpos(tree,arr[i]);
                if(pos<=next[i]-1)
                    update(tree,arr[i],pos,next[i]-1);
            }
            update(tree,0,i,i);
        }
        cout<<ans<<"\n";
    }
    return 0;
}
时间: 2024-11-03 10:15:33

hdu4747 mex 线段树的相关文章

hdu4747(线段树区间更新)

Mex Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 1892    Accepted Submission(s): 625 Problem Description Mex is a function on a set of integers, which is universally used for impartial game

BZOJ.3585.mex(线段树)

题目链接 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么\([l+1,r]\)中所有\(>a[l]\)的数显然要改成\(a[l]\) 询问排序,离散化,预处理下nxt[],剩下就是线段树的区间更新.查询了 /* 离散化的时候>=n的全部看做n就好了 查询时是只需查r点的(l之前能更新r的已经更新完了,初始时是[1,r],r点现在就是[l,r]了) 单点即可不需要PushUp(也不好得某

CF1083C Max Mex 线段树

题面 CF1083C Max Mex 题解 首先我们考虑,如果一个数x是某条路径上的mex,那么这个数要满足什么条件? 1 ~ x - 1的数都必须出现过. x必须没出现过. 现在我们要最大化x,那么也就意味着我们要找到一条路径使得这个都出现过的前缀尽可能长. 第二个条件可以忽略,因为如果第1个条件满足,而第2个条件却不满足,意味着我们可以把x至少扩大1位,因为要求最大值,所以扩大肯定最优,因此我们肯定会扩大到不能扩大为止. 由此我们可以发现,x是满足可二分性的. 考虑在线段树上维护这个问题,区

hdu 4747 Mex( 线段树? 不,区间处理就行(dp?))

Mex Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 3056    Accepted Submission(s): 1006 Problem Description Mex is a function on a set of integers, which is universally used for impartial game

HDU-4747 Mex(线段树区间更新)

题目大意:给一个长度为n的整数序列,定义mex(i,j)表示区间[i,j]中没有出现过的最小非负整数,求sigma(mex(i,j)),即序列中所有连续非空子区间的mex之和. 题目分析: answer=mex(1,1)+mex(1,2)...mex(1,n) + mex(2,2)...mex(2,n) . . . + mex(n,n). 初始时,用线段树的叶子节点维护mex(1,i),将a(1)从序列中拿去后,将叶子节点的维护信息更新为mex(2,i),以此类推...没更新一次,便求一次区间和

【线段树】HDU 4747 MEX

通道:http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意:mex(L, R)表示区间上第一个没出现的最小非负整数,对于序列a[],求所有的mex(L, R)的和 思路:就是求mex(1,1) + mex(1,2)+....+mex(1,n) +mex(2,2) + mex(2,3) + ...mex(2,n) +mex(3,3) + mex(3,4)+...+mex(3,n) ...+ mex(n,n) 可以知道mex(z,i),mex(z,i+1

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

Mex(线段树的巧妙应用)

题目要求求某段区间第一个没有出现的数(0,1,2,3....) ,对于所有的区间,我们把这样的数加起来最后得到一个结果. 首先,我们要求出这样的数,然后还得列举出所有的区间,复杂度太大了. 换种思路,我们定住L,是不是一次性能求出所有的R所得出的结果,这就用到线段树的性质了,因为在移动L的过程中,移一步只变化一个数,那么就可以用线段树进行维护. 首先求出[1,R] 以1为左端的所有区间的情况,记录每个点也就是1到那个点的这段区间值sum[i],以这个值建一颗树,那么在L向前移动的时候,每次丢掉一

mex (离散化+线段树)

Time Limit: 3000 ms   Memory Limit: 256 MB Description 给你一个无限长的数组,初始的时候都为0,有3种操作: 操作1是把给定区间$[l,r]$设为1, 操作2是把给定区间$[l,r]$设为0, 操作3把给定区间$[l,r]$0,1反转. 一共n个操作,每次操作后要输出最小位置的0. Input 第一行一个整数n,表示有n个操作 接下来n行,每行3个整数op,l,r表示一个操作 Output 共n行,一行一个整数表示答案 Sample Inpu