青青草原播种计划

题目大意

n n 个可重集,支持下列操作:
往某个集合加入某个数。
让某个集合删除某个数。
将一个集合的所有数放入另一个集合中(然后该集合就空了)。
询问一个集合的mex以及最小的x使得 x x <script type="math/tex" id="MathJax-Element-1172">x</script>无法表示成子集和。
对于历史版本进行询问。
强制在线。

做法

1是加入,2是删除,3是合并。
值域线段树都能支持。
mex用值域线段树做。
第二个的话,可以考虑把值从小到大排序,如果一个值大于它前一个值的前缀和+1,找到最小的这样的位置,答案就是前一个值的前缀和+1。
线段树节点维护和与一个值-前缀和最大值,询问线段树二分即可。
历史版本的话,每个集合维护一个set来表示对应线段树根的分段函数。
因此线段树要可持久化。

#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10,maxtot=15000000+10;
struct dong{
    int x,y;
    bool friend operator <(dong a,dong b){
        return a.x<b.x||a.x==b.x&&a.y<b.y;
    }
} zlt;
set<dong> his[maxn];
set<dong>::iterator it;
ll tree[maxtot],sum[maxtot];
int left[maxtot],right[maxtot],root[maxn];
bool bz[maxtot];
int i,j,k,l,t,n,m,tot,ans,mx,tim;
ll wdc;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int newnode(int x){
    tot++;
    tree[tot]=tree[x];
    sum[tot]=sum[x];
    left[tot]=left[x];
    right[tot]=right[x];
    bz[tot]=bz[x];
    return tot;
}
void insert(int &x,int l,int r,int a,int b){
    x=newnode(x);
    if (l==r){
        if (!sum[x]&&b==-1) return;
        sum[x]+=(ll)a*b;
        if (sum[x]) bz[x]=1;else bz[x]=0;
        if (bz[x]) tree[x]=a-1;else tree[x]=0;
        return;
    }
    int mid=(l+r)/2;
    if (a<=mid) insert(left[x],l,mid,a,b);else insert(right[x],mid+1,r,a,b);
    sum[x]=sum[left[x]]+sum[right[x]];
    bz[x]=bz[left[x]]&bz[right[x]];
    tree[x]=max(tree[left[x]],tree[right[x]]-sum[left[x]]);
}
int merge(int x,int y,int l,int r){
    if (!x||!y) return x+y;
    if (l==r){
        x=newnode(x);
        sum[x]+=sum[y];
        if (sum[x]) bz[x]=1;else bz[x]=0;
        if (bz[x]) tree[x]=l-1;else tree[x]=0;
        return x;
    }
    int mid=(l+r)/2;
    x=newnode(x);
    left[x]=merge(left[x],left[y],l,mid);
    right[x]=merge(right[x],right[y],mid+1,r);
    sum[x]=sum[left[x]]+sum[right[x]];
    bz[x]=bz[left[x]]&bz[right[x]];
    tree[x]=max(tree[left[x]],tree[right[x]]-sum[left[x]]);
    return x;
}
void query(int x,int l,int r){
    if (bz[x]){
        ans=r+1;
        return;
    }
    if (l==r){
        ans=l;
        return;
    }
    int mid=(l+r)/2;
    if (bz[left[x]]) query(right[x],mid+1,r);else query(left[x],l,mid);
}
void ask(int x,int l,int r){
    if (tree[x]<=wdc){
        wdc+=sum[x];
        wdc++;
        return;
    }
    if (l==r){
        wdc++;
        return;
    }
    int mid=(l+r)/2;
    if (tree[left[x]]<=wdc){
        wdc+=sum[left[x]];
        ask(right[x],mid+1,r);
    }
    else ask(left[x],l,mid);
}
void travel(int x,int l,int r){
    if (!x) return;
    if (l==r){
        while (sum[x]){
            printf("%d ",l);
            sum[x]-=(ll)l;
        }
        return;
    }
    int mid=(l+r)/2;
    travel(left[x],l,mid);
    travel(right[x],mid+1,r);
}
int main(){
    freopen("forgive.in","r",stdin);freopen("forgive.out","w",stdout);
    mx=500000;
    n=read();m=read();
    fo(i,1,n){
        zlt.x=0;
        zlt.y=0;
        his[i].insert(zlt);
    }
    while (m--){
        tim++;
        t=read();j=read();
        j=(j+ans-1)%n+1;
        if (t==1){
            k=read();
            insert(root[j],1,mx,k,1);
            zlt.x=tim;
            zlt.y=root[j];
            his[j].insert(zlt);
        }
        else if (t==2){
            k=read();
            insert(root[j],1,mx,k,-1);
            zlt.x=tim;
            zlt.y=root[j];
            his[j].insert(zlt);
        }
        else if (t==3){
            k=read();
            k=(k+ans-1)%n+1;
            if (j!=k){
                root[j]=merge(root[j],root[k],1,mx);
                root[k]=0;
                zlt.x=tim;
                zlt.y=root[j];
                his[j].insert(zlt);
                zlt.y=root[k];
                his[k].insert(zlt);
            }
        }
        else if (t==4){
            query(root[j],1,mx);
            wdc=0;
            ask(root[j],1,mx);
            printf("%d %lld\n",ans,wdc);
        }
        else if (t==5){
            k=read();
            k--;
            zlt.x=k;
            zlt.y=tot+1;
            his[j].insert(zlt);
            it=his[j].lower_bound(zlt);
            it--;
            k=(*it).y;
            query(k,1,mx);
            wdc=0;
            ask(k,1,mx);
            printf("%d %lld\n",ans,wdc);
            it++;
            his[j].erase(it);
        }
    }
    fo(i,1,n){
        travel(root[i],1,mx);
        printf("0\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值