HDU - 4747 Mex(线段树)

博弈论与线段树算法

题意:

计算  \sum_{i=1}^{n}\sum_{j=i}^{n}mex(i,j)  其中mex即为博弈中出现的mex(未出现的最小非负整数)。

分析:

有两种方法,递推有点懵(以后再来补QAQ),就写了线段树

想法是每次求以i为起点的区间的mex值的和,最后累加即为答案。

可以先预处理出1为起点的区间的mex值,用它构造一棵线段树,要有求和和求最大值的操作。

构造好线段树之后,1为起点的区间的mex和已经得到了,就是sum[1]。

接下来求2为起点的,首先第一步要删掉a[1],这里把mex[1]置为0(区间求和不再受影响)。

删掉val=a[1]这个元素对后面的有什么影响呢?易知它不会对下一个出现val(下一个出现val的位置是next[1])之后的位置产生影

响,因为后面的区间已经包含val了(不少它这一个)

然后我们需要求得大于val的最大mex的位置pos,线段树单点查询最大值即可得到。

对于pos到next[1] - 1的元素,所有的mex值都要变成a[i](不难想),成段更新线段树即可。

这样就得到了2为起点的区间的mex和,sum[1]。

 

#include<bits/stdc++.h>

using namespace std;

const int maxn=2e5+10;

typedef long long ll;

struct node{
    ll mex,sum;
    bool lazy;
}tree[maxn<<2];

ll a[maxn],tmp[maxn];

void pushdown(int p,int l,int r){
    if(!tree[p].lazy) return;
    int mid=l+r>>1;
    tree[p<<1].sum=tree[p].mex*1ll*(mid-l+1);
    tree[p<<1|1].sum=tree[p].mex*1ll*(r-mid);
    tree[p<<1].mex=tree[p<<1|1].mex=tree[p].mex;
    tree[p<<1].lazy=tree[p<<1|1].lazy=1;
    tree[p].lazy=0;
}

void pushup(int p){
    tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum;
    tree[p].mex=max(tree[p<<1].mex,tree[p<<1|1].mex);
}

void build(int p,int l,int r){
    tree[p].lazy=0;
    if(l==r){
        tree[p].sum=tree[p].mex=tmp[l];
        return;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}

void update(int tl,int tr,ll val,int l,int r,int p){
    if(tl>r||tr<l) return;
    if(tl<=l&&r<=tr){
        tree[p].sum=(r-l+1)*1ll*val;
        tree[p].mex=val;
        tree[p].lazy=true;
        return;
    }
    pushdown(p,l,r);
    int mid=l+r>>1;
    update(tl,tr,val,l,mid,p<<1);
    update(tl,tr,val,mid+1,r,p<<1|1);
    pushup(p);
}

int query(ll val,int l,int r,int p){
    if(l==r){
        return l;
    }
    pushdown(p,l,r);
    int mid=l+r>>1;
    if(val<tree[p<<1].mex)
        return query(val,l,mid,p<<1);
    else return query(val,mid+1,r,p<<1|1);

}

int head[maxn],nex[maxn];
bool vis[maxn];

int main(){

    int n;
    while(scanf("%d",&n)&&n){
        memset(head,0x3f,sizeof head);
        memset(vis,0,sizeof vis);
        int k=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            if(a[i]>n) a[i]=n+1;
            vis[a[i]]=1;
            for(int j=k;j<=n;j++){
                if(!vis[j]){
                    tmp[i]=j;
                    k=j;
                    break;
                }
            }
        }
        for(int i=n;i>0;i--){
            nex[i]=head[a[i]];
            head[a[i]]=i;
        }
        build(1,1,n);

        ll ans=0;
        ans+=tree[1].sum;
        for(int i=1;i<=n;i++){
            update(i,i,0ll,1,n,1);
            if(tree[1].mex>a[i]){
                int pos=query(a[i],1,n,1);
                if(pos<nex[i]){
                    update(pos,nex[i]-1,a[i],1,n,1);
                }
            }
            ans+=tree[1].sum;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值