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;
}

發佈了217 篇原創文章 · 獲贊 10 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章