HDU 4747 Mex 递推/线段树

会有一股神秘感。 提交于 2020-03-09 16:02:40

题目链接:

acm.hdu.edu.cn/showproblem.php?pid=4747

Mex


Time Limit: 15000/5000 MS (Java/Others)Memory Limit: 65535/65535 K (Java/Others)

问题描述

Mex is a function on a set of integers, which is universally used for impartial game theorem. For a non-negative integer set S, mex(S) is defined as the least non-negative integer which is not appeared in S. Now our problem is about mex function on a sequence.

Consider a sequence of non-negative integers {ai}, we define mex(L,R) as the least non-negative integer which is not appeared in the continuous subsequence from aL to aR, inclusive. Now we want to calculate the sum of mex(L,R) for all 1 <= L <= R <= n.

输入

The input contains at most 20 test cases.
For each test case, the first line contains one integer n, denoting the length of sequence.
The next line contains n non-integers separated by space, denoting the sequence.
(1 <= n <= 200000, 0 <= ai <= 10^9)
The input ends with n = 0.

输出

For each test case, output one line containing a integer denoting the answer.

样例输入

3
0 1 3
5
1 0 2 0 1
0

样例输出

5
24

Hint

For the first test case:
mex(1,1)=1, mex(1,2)=2, mex(1,3)=2, mex(2,2)=0, mex(2,3)=0,mex(3,3)=0.
1 + 2 + 2 + 0 +0 +0 = 5.

题意

mex(s)表示没有在集合s中出现的最小非负数,现在要求所有的mex(si)(si为由数列的一个子串组成的集合)的和。

题解

1、递推、 [参考]

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);

//start----------------------------------------------------------------------

const int maxn=2e5+10;

int n;
int arr[maxn];
//last[x]表示x这个数最晚出现的位置
//full[x]表示完全覆盖0到x的且左边界最靠右的区间
int last[maxn],full[maxn];
//dp[i]表示以i结尾的所有区间的贡献
LL dp[maxn];

void init(){
    clr(dp,0);
    clr(full,0);
    clr(last,0);
}

int main() {
    while(scf("%d",&n)==1&&n){
        init();
        for(int i=1;i<=n;i++) scf("%d",&arr[i]);
        LL ans=0;
        for(int i=1;i<=n;i++){
            dp[i]=dp[i-1];
            if(arr[i]<n){
                int v=arr[i];
                int pre=last[v];
                last[v]=i;
                for(int j=v;j<n;j++){
                    if(j) full[j]=min(last[j],full[j-1]);
                    else full[j]=i;
                    if(full[j]>pre) dp[i]+=full[j]-pre;
                    else break;
                }
            }
            ans+=dp[i];
        }
        prf("%lld\n",ans);
    }
    return 0;
}

//end-----------------------------------------------------------------------

2、线段树
初始时线段树上第i个节点存mex(a[1]~a[i])(注意!这个存在单调性!)。
然后我们依次从a[1]开始删除,直到删到a[n-1]。 如果删掉a[1],那么我们对于原本mex值就小于等于a[1]的节点自然没有影响,而对于mex>a[1]且其它位置不存在a[1]的元素,删掉这个a[1]之后它的mex值就会等于a[1],所以我们只要用线段树update一下区间[第一个mex值大于a[1](根据单调性在线段树上二分查找)的位置,下一个值等于a[1]的位置-1]。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);

//start----------------------------------------------------------------------

const int maxn=2e5+10;

LL sumv[maxn<<2];
int mexv[maxn<<2],setv[maxn<<2];
int arr[maxn],b[maxn];
bool vis[maxn];
int mp[maxn],last[maxn];
int n;

void pushdown(int o,int l,int r) {
    if(setv[o]>=0) {
        sumv[lson]=(mid-l+1)*setv[o];
        mexv[lson]=setv[o];

        sumv[rson]=(r-mid)*setv[o];
        mexv[rson]=setv[o];

        setv[lson]=setv[rson]=setv[o];
        setv[o]=-1;
    }
}

void maintain(int o) {
    sumv[o]=sumv[lson]+sumv[rson];
    mexv[o]=max(mexv[lson],mexv[rson]);
}

void build(int o,int l,int r) {
    if(l==r) {
        sumv[o]=mexv[o]=b[l];
    } else {
        build(lson,l,mid);
        build(rson,mid+1,r);
        maintain(o);
    }
}

int _v,_pos;
void query(int o,int l,int r) {
    if(l==r) {
        if(_v>=mexv[o]) _pos=n+1;
        else _pos=l;
    } else {
        pushdown(o,l,r);
        if(_v>=mexv[lson]) query(rson,mid+1,r);
        else query(lson,l,mid);
        maintain(o);
    }
}

int ql,qr,_v2;
void update(int o,int l,int r) {
    if(ql<=l&&r<=qr) {
        sumv[o]=(r-l+1)*_v2;
        mexv[o]=_v2;
        setv[o]=_v2;
    } else {
        pushdown(o,l,r);
        if(ql<=mid) update(lson,l,mid);
        if(qr>mid) update(rson,mid+1,r);
        maintain(o);
    }
}

void init() {
    clr(vis,0);
    clr(setv,-1);
    for(int i=0; i<=n; i++) mp[i]=last[i]=n+1;
}

int main() {
    while(scf("%d",&n)==1&&n) {
        init();
        int mex=0;
        for(int i=1; i<=n; i++) {
            int x;
            scf("%d",&x);
            arr[i]=x;
            if(x<n) vis[x]=1;
            while(vis[mex]) mex++;
            b[i]=mex;
        }

        for(int i=n; i>=1; i--) {
            if(arr[i]<n) {
                mp[i]=last[arr[i]]-1;
                last[arr[i]]=i;
            }
        }

//        for(int i=1;i<=n;i++) prf("%d ",b[i]); puts("");
        build(1,1,n);

        LL ans=sumv[1];
//        bug(ans);
        for(int i=1; i<n; i++) {

            if(arr[i]>=n) {
                ans+=sumv[1];
            } else {
                _v=arr[i];
                query(1,1,n);
//                prf("%d,%d\n",_pos,mp[i]);

                if(_pos<=mp[i]){
                    ql=_pos,qr=mp[i];
                    _v2=arr[i];
                    update(1,1,n);
                }
                ans+=sumv[1];
            }
//            bug(ans);
        }

        prf("%lld\n",ans);
    }
    return 0;
}

//end-----------------------------------------------------------------------  

Notes

枚举区间: dp[i]表示以i为右边界的所有区间的贡献。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!