HDU4747 – Mex(线段树+离散)

传送门:HDU4747

【题意】给出一个N(<=200000)长的序列,定义一个MEX(L,R)表示区间[L,R]中没有出现的最小非负整数,求出序列所有MEX()之和。

【分析】首先很容易看出对于L固定的区间,R的向右是单调递增的,这样可以先预处理出第一行[L,1][L,2][L,3]…[L,N],把它放入线段树,然后观察左边每删掉一个数(也就是下一个L)对后面的影响,当删除a[i]后,一直到下一个a[i]出现前的大于a[i]区间答案都要变为a[i],下一个出现的a[i]可以预处理出来,然后由于是递增的,所以只要在线段树二分找到第一个大于a[i]的作为左端点x,下一个出现的a[i]前一个作为右端点y,把[x,y]全部更新为a[i]即可。对于预处理,我把它离散化了(需要注意连续离散两个之间差>1的情况)。看了bin神直接用map的,map更加简单呀。

【AC CODE】1950ms

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 200000+10;
int a[MAXN],num[MAXN],sot[MAXN<<1],cnt;

int mhash(int v){return lower_bound(sot,sot+cnt,v)-sot;}
int inline id(int x, int y){return x+y|x!=y;}
int sz[MAXN<<2];
void add(int x, int y, int p)
{
    if(x == y)
    {
        sz[id(x,y)] = 1;
        return;
    }
    int m = (x+y)>>1;
    if(p <= m) add(x,m,p);
    else add(m+1,y,p);
    sz[id(x,y)] = sz[id(x,m)]+sz[id(m+1,y)];
}
int query(int x, int y)
{
    if(x == y) return sot[x];
    int m = (x+y)>>1;
    if(sz[id(x,m)] < m-x+1) return query(x,m);
    return query(m+1,y);
}
int d[MAXN],mx[MAXN],n;
LL sum[MAXN<<1],st[MAXN<<1];
void push_down(int x, int y, int m)
{
    int u = id(x,y);
    if(~st[u])
    {
        int l = id(x,m),r = id(m+1,y);
        st[l] = st[r] = st[u];
        sum[l] = (m-x+1)*st[u];sum[r] = (y-m)*st[u];
        mx[l] = mx[r] = st[u];
        st[u] = -1;
    }
}
void push_up(int x, int y, int m)
{
    int u = id(x,y),l = id(x,m),r = id(m+1,y);
    sum[u] = sum[l]+sum[r];
    mx[u] = max(mx[l],mx[r]);
}
void bulid(int x, int y)
{
    if(x == y)
    {
        sum[id(x,y)] = mx[id(x,y)] = d[x];
        return;
    }
    int m = (x+y)>>1;
    bulid(x,m);
    bulid(m+1,y);
    push_up(x,y,m);
}
void update(int x, int y, int ql, int qr, int v)
{
    if(ql <= x && y <= qr)
    {
        int u = id(x,y);
        sum[u] = v*(y-x+1);
        mx[u] = v,st[u] = v;
        return;
    }
    int m = (x+y)>>1;
    push_down(x,y,m);
    if(ql <= m) update(x,m,ql,qr,v);
    if(qr > m) update(m+1,y,ql,qr,v);
    push_up(x,y,m);
}
int find_ft(int x, int y, int v)
{
    if(x == y) return x+(v>sum[id(x,y)]);
    int m = (x+y)>>1,ans;
    push_down(x,y,m);
    if(v < mx[id(x,m)]) ans = find_ft(x,m,v);
    else ans = find_ft(m+1,y,v);
    push_up(x,y,m);
    return ans;
}
int nxt[MAXN],la[MAXN<<1];

int main()
{
#ifdef SHY
    freopen("d:\\1.txt", "r", stdin);
#endif
    while(~scanf("%d", &n) && n)
    {
        rep(i,0,n) scanf("%d", &a[i]), num[i] = a[i];
        num[n] = 0;
        sort(num,num+n+1);
        cnt = 1;
        sot[0] = num[0];
        rep(i,0,n+1)
        {
            if(num[i] != num[i-1])
            {
                sot[cnt++] = num[i];
                if(num[i]-num[i-1] > 1)
                {
                    sot[cnt++] = num[i-1]+1;
                    swap(sot[cnt-1],sot[cnt-2]);
                }
            }
        }
        sot[cnt] = sot[cnt-1]+1;
        cnt++;
        clc(sz,0);
        clc(la,-1);
        rep(i,0,n) nxt[i] = n-1;
        rep(i,0,n)
        {
            int h = mhash(a[i]);
            add(0,cnt-1,h);
            d[i] = query(0,cnt-1);
            if(~la[h]) nxt[la[h]] = i-1;
            la[h] = i;
        }
        clc(st,-1);
        bulid(0,n-1);
        LL ans = 0;
        rep(i,0,n)
        {
            ans += sum[id(0,n-1)];
            int y = nxt[i];
            int x = find_ft(0,n-1,a[i]);
            update(0,n-1,0,i,0);
            if(x > y) continue;
            update(0,n-1,x,y,a[i]);
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

请输入正确的验证码