题目大意
有
n
n
个可重集,支持下列操作:
往某个集合加入某个数。
让某个集合删除某个数。
将一个集合的所有数放入另一个集合中(然后该集合就空了)。
询问一个集合的mex以及最小的使得
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");
}
}