魔法少女LJJ
Description :
题目描述
在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了
LJJ感叹道“这里真是个迷人的绿色世界,空气清新、淡雅,到处散发着醉人的奶浆味;小猴在枝头悠来荡去,好不自在;各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果;鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”
SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树,动态仙人掌了,那么今天就来见识一下动态图吧”
LJJ:“要支持什么操作?”
SHY:“
1.新建一个节点,权值为x。
2.连接两个节点。
3.将一个节点a所属于的联通快内权值小于x的所有节点权值变成x。
4.将一个节点a所属于的联通快内权值大于x的所有节点权值变成x。
5.询问一个节点a所属于的联通块内的第k小的权值是多少。
6.询问一个节点a所属联通快内所有节点权值之积与另一个节点b所属联通快内所有节点权值之积的大小。
7.询问a所在联通快内节点的数量
8.若两个节点a,b直接相连,将这条边断开。
9.若节点a存在,将这个点删去。
”
LJJ:“我可以离线吗?”
SHY:“可以,每次操作是不加密的,”
LJJ:“我可以暴力吗?”
SHY:“自重”
LJJ很郁闷,你能帮帮他吗
数据范围及提示:
$ 1\le m \le 4\times 10^{5},opt \le 7$
Solution:
我们会发现后面两种恶心到令人发指的操作根本不用实现(\(opt \le 7\))。也就是说,我们只需要合并两个联通块就好了,这让我们很难不想到并查集.
我们维护一些并查集以支持连边操作,然后每个并查集上开一颗权值线段树,每次连边就将两颗树合并。但是我们会发现乘积貌似有点难维护,但是题目只要求比大小,所以我们并不需要维护乘积的真实值,维护对数就好了。
然后修改操作就在 \([1,x-1]\) 或者是 \([x+1,inf]\) 区间上打上删除标记并且记录删除了多少个数字 \(tmp\),然后在 \(x\) 节点上添加 \(tmp\) 个点就好了。
细节提示:
由于这题的删除标记是要 pushdown 的,所以我们在 merge 时一定要将 x,y 都 pushdown
然后就是不推荐使用太过先进的编译器。因为在并查集时,你的 find 函数就算没有 return ,编译器也可能帮你返回一个你最后调用的变量。例如:
int find(int x){fa[x] = fa[x]==x ? fa[x] : find(fa[x]);}
在我的编译器上是会直接返回 fa[x] 的,而不会报错。这两个致命错误导致我在本地调了 3h+
(╯°Д°)╯︵ ┻━┻
然后这题就做完了
Code:
#include<bits/stdc++.h>
const int N=4e5+5;
const int inf=1e9;
using namespace std;
int n,tot;
struct Segment_Tree{struct Tree{int ls,rs,cnt,tag;double val;}t[N*40];int cnt,rt[N];void erase(int x){t[x].cnt=0,t[x].val=0,t[x].tag=1;}void pushdown(int x){if(!t[x].tag)return;erase(t[x].ls),erase(t[x].rs);t[x].tag=0;}void pushup(int x){t[x].cnt=t[t[x].ls].cnt+t[t[x].rs].cnt;t[x].val=t[t[x].ls].val+t[t[x].rs].val;}int merge(int x,int y,int l,int r){if(!x||!y)return x|y;if(l==r){t[x].cnt+=t[y].cnt,t[x].val+=t[y].val;return x;}int mid=l+r>>1;pushdown(x);pushdown(y);t[x].ls=merge(t[x].ls,t[y].ls,l,mid);t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);pushup(x);return x;}void insert(int &x,int l,int r,int pos,int k){x=(x ? x : ++cnt);if(l==r){t[x].cnt+=k;t[x].val+=1.0*k*log(pos);return;}int mid=l+r>>1;pushdown(x);if(pos<=mid)insert(t[x].ls,l,mid,pos,k);if(mid<pos) insert(t[x].rs,mid+1,r,pos,k);pushup(x);}void upd(int x,int l,int r,int L,int R,int &res){if(L<=l&&r<=R){res+=t[x].cnt;erase(x);return;}int mid=l+r>>1;pushdown(x);if(L<=mid)upd(t[x].ls,l,mid,L,R,res);if(mid<R) upd(t[x].rs,mid+1,r,L,R,res);pushup(x);}int query(int x,int l,int r,int k){pushdown(x);if(l==r)return l;int mid=l+r>>1;if(k<=t[t[x].ls].cnt)return query(t[x].ls,l,mid,k);else return query(t[x].rs,mid+1,r,k-t[t[x].ls].cnt);}
}T;
int fa[N];
int find(int x){return fa[x] = fa[x]==x ? fa[x] : find(fa[x]);}
void work()
{cin>>n;for(int i=1,opt,x,y;i<=n;i++){scanf("%d%d",&opt,&x);if(1<opt&&opt<7)scanf("%d",&y);if(opt==1){tot++;fa[tot]=tot;T.insert(T.rt[tot],1,inf,x,1);}if(opt==2){int u=find(x),v=find(y);if(u!=v){fa[v]=u;T.rt[u]=T.merge(T.rt[u],T.rt[v],1,inf);}}if(opt==3){int tmp=0;x=find(x);T.upd(T.rt[x],1,inf,1,y-1,tmp);T.insert(T.rt[x],1,inf,y,tmp);}if(opt==4){int tmp=0;x=find(x);T.upd(T.rt[x],1,inf,y+1,inf,tmp);T.insert(T.rt[x],1,inf,y,tmp);}if(opt==5){x=find(x);int ans=T.query(T.rt[x],1,inf,y);printf("%d\n",ans);}if(opt==6){x=find(x),y=find(y);printf("%d\n",(T.t[T.rt[x]].val > T.t[T.rt[y]].val ? 1 : 0));}if(opt==7){x=find(x);printf("%d\n",T.t[T.rt[x]].cnt);}}
}
int main()
{freopen("girl.in","r",stdin);freopen("girl.out","w",stdout);work();return 0;
}