HDU - 4747

博客围绕HDU - 4747题目展开,指出枚举右端点维护每个位置的mex较复杂,转而考虑删除左端点维护前缀mex。分析当前位置影响,可通过线段树维护区间修改,因答案单调递增,线段树区间赋值即可,最后给出AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:HDU - 4747


每次枚举右端点,加入一个数字,维护每个位置的mex,显然很复杂。
所以我们考虑每次删除一个左端点,维护前缀mex。
考虑当前位置的影响,会影响到后面大于 a[i] 的mex,会变成 a[i] ,直到下一个 a[i] ,所以线段树维护区间修改即可,因为答案是单调递增的,所以线段树区间赋值即可,不用segment beats。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int n,mx[N<<2],a[N],sum[N<<2],lazy[N<<2],mex[N],cnt,pos[N],res; map<int,int> mp,vis;
#define mid (l+r>>1)
inline void push_up(int p){
    sum[p]=sum[p<<1]+sum[p<<1|1];
    mx[p]=max(mx[p<<1],mx[p<<1|1]);
}
void build(int p,int l,int r){
    lazy[p]=-1;
    if(l==r){mx[p]=sum[p]=mex[l]; return ;}
    build(p<<1,l,mid),build(p<<1|1,mid+1,r);
    push_up(p);
}
inline void upd(int p,int l,int r,int v){sum[p]=(r-l+1)*v,mx[p]=v,lazy[p]=v;}
inline void push_down(int p,int l,int r){
    if(lazy[p]!=-1) upd(p<<1,l,mid,lazy[p]),upd(p<<1|1,mid+1,r,lazy[p]),lazy[p]=-1;
}
void change(int p,int l,int r,int ql,int qr,int v){
    if(l==ql&&r==qr) return upd(p,l,r,v),void();
    push_down(p,l,r);
    if(qr<=mid) change(p<<1,l,mid,ql,qr,v);
    else if(ql>mid) change(p<<1|1,mid+1,r,ql,qr,v);
    else change(p<<1,l,mid,ql,mid,v),change(p<<1|1,mid+1,r,mid+1,qr,v);
    push_up(p);
}
int ask(int p,int l,int r,int ql,int qr){
    if(l==ql&&r==qr) return sum[p];
    push_down(p,l,r);
    if(qr<=mid) return ask(p<<1,l,mid,ql,qr);
    else if(ql>mid) return ask(p<<1|1,mid+1,r,ql,qr);
    else return ask(p<<1,l,mid,ql,mid)+ask(p<<1|1,mid+1,r,mid+1,qr);
} 
int query(int p,int l,int r,int ql,int qr,int v){
    if(l==r) return l;
    push_down(p,l,r);
    if(qr<=mid) return query(p<<1,l,mid,ql,qr,v);
    else if(ql>mid) return query(p<<1|1,mid+1,r,ql,qr,v);
    else{
        if(mx[p<<1]>v) return query(p<<1,l,mid,ql,mid,v);
        else return query(p<<1|1,mid+1,r,mid+1,qr,v);
    }
}
void solve(){
    cnt=res=0; vis.clear(); mp.clear();
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=n;i>=1;i--){
        if(!mp.count(a[i])) pos[i]=n+1;
        else pos[i]=mp[a[i]];
        mp[a[i]]=i;
    }
    for(int i=1;i<=n;i++){
        vis[a[i]]=1;
        while(vis.count(cnt)) cnt++;
        mex[i]=cnt;
    }
    build(1,1,n);
    for(int i=1;i<=n;i++){
        res+=ask(1,1,n,i,n);
        if(ask(1,1,n,pos[i]-1,pos[i]-1)<=a[i]) continue;
        int p=query(1,1,n,i,n,a[i]);
        if(p<pos[i]) change(1,1,n,p,pos[i]-1,a[i]);
    }
    cout<<res<<endl;
}
signed main(){
    while(cin>>n,n) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值