Codeforces 1436 E. Complicated Computations —— 树套树

This way

题意:

定义MEX:当前数组中第一个未出现过的正数
给你一个长度为n的串,用它的所有子串的MEX组成一个新串,问你这个新串的MEX是什么

题解:

我在怀疑为什么能有接近1k的人做出来,原来可以离线做…我忘了
很明显是枚举每个MEX是否存在,也就是查看是否有一个区间包含1~(x-1)而不包含x,于是就是检查每两个x之间是否有1~(x-1)的排列。那么肯定是从小到大枚举MEX,然后检查之后如果存在的话,将其加入到已有的数组中,然后再检查下一个。
那么怎么知道是有1~(x-1)的排列,这就想到了主席树中——区间有多少个不同的数,因为我是从小到大做MEX,做完之后加入当前数的影响。然后如果区间不同数有x-1个,就说明存在
那么区间不同数的个数就按照普通主席树去做就好了,但是普通主席树不支持修改,那怎么办?很明显是树套树
树套树不会的话,我有博客讲。
那么
在这里插入图片描述
update就是说,root这个点,p位置的值+v
在这里插入图片描述
query就是说,到root这个点的时候,区间ql~qr有多少个不同的值
在这里插入图片描述
那么update的时候,只要将当前位置上一个相同值的位置值-1,然后再在当前位置的值+1即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct Chairman{
    int ls[N*220],rs[N*220],rt[N],num[N*220],tot;
    void update(int l,int r,int &root,int p,int v){
        if(!root)root=++tot;
        num[root]+=v;
        if(l==r)return ;
        int mid=l+r>>1;
        if(mid>=p)
            update(l,mid,ls[root],p,v);
        else
            update(mid+1,r,rs[root],p,v);
    }
    int query(int l,int r,int *root,int ql,int qr){
        int sum=0,mid=l+r>>1;
        if(l>=ql&&r<=qr){
            for(int i=1;i<=root[0];i++)sum+=num[root[i]];
            return sum;
        }
        int ans=0,nr[25];
        nr[0]=root[0];
        if(mid>=ql){
            for(int i=1;i<=root[0];i++)nr[i]=ls[root[i]];
            ans=query(l,mid,nr,ql,qr);
        }
        if(mid<qr){
            for(int i=1;i<=root[0];i++)nr[i]=rs[root[i]];
            ans+=query(mid+1,r,nr,ql,qr);
        }
        return ans;
    }
}cmt;
int all,n,m;
int lowbit(int x){return x&(-x);}
void update(int l,int r,int root,int p,int v){
    for(int i=root;i<=n;i+=lowbit(i)){
        cmt.update(l,r,cmt.rt[i],p,v);
    }
}
int root[50],last[50];
int a[N];
int query(int y,int l,int r){
    //int x=op[id].x,y=op[id].y,l=op[id].l,r=op[id].r;
    root[0]=last[0]=0;
    for(int i=y;i;i-=lowbit(i))
        root[0]++,root[root[0]]=cmt.rt[i];
    return cmt.query(1,n,root,l,r);
}
vector<int>pos[N];
int main()
{
    //int n;
    scanf("%d",&n);
    int f=0;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),update(1,n,1,i,0),f+=a[i]!=1,pos[a[i]].push_back(i);
    if(!f)return 0*printf("1\n");
    if(f==n)return 0*printf("2\n");
    for(int i=2;i<=n+1;i++){
        //if(pos[i-1].empty())return 0*printf("%d\n",i);
        if(pos[i].empty())
            return 0*printf("%d\n",i+1);
        update(1,n,pos[i-1][0],pos[i-1][0],1);
        //printf("%d\n",pos[i-1][0]);
        int sz=pos[i-1].size();
        for(int j=1;j<sz;j++)
            update(1,n,pos[i-1][j],pos[i-1][j-1],-1),update(1,n,pos[i-1][j],pos[i-1][j],1);
        if(pos[i][0]!=1){
            int sum=query(pos[i][0]-1,1,pos[i][0]-1);
            //printf("%d\n",pos[i][0]);
            if(sum==i-1)continue;
        }
        sz=pos[i].size();
        f=0;
        for(int j=1;j<sz;j++){
            if(pos[i][j]-pos[i][j-1]<=1)continue;
            int sum=query(pos[i][j]-1,pos[i][j-1]+1,pos[i][j]-1);
            if(sum==i-1){
                f=1;
                break;
            }
        }
        if(f)continue;
        if(pos[i][sz-1]!=n){
            int sum=query(n,pos[i][sz-1]+1,n);
            if(sum==i-1)continue;
        }
        return 0*printf("%d\n",i);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值