BZOJ4399魔法少女LJJ——线段树合并+并查集
题目描述
在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女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很郁闷,你能帮帮他吗
输入
第一行有一个正整数m,表示操作个数。
接下来m行,每行先给出1个正整数c。
若c=1,之后一个正整数x,表示新建一个权值为x的节点,并且节点编号为n+1(当前有n个节点)。
若c=2,之后两个正整数a,b,表示在a,b之间连接一条边。
若c=3,之后两个正整数a,x,表示a联通快内原本权值小于x的节点全部变成x。
若c=4,之后两个正整数a,x,表示a联通快内原本权值大于x的节点全部变成x。
若c=5,之后两个正整数a,k,表示询问a所属于的联通块内的第k小的权值是多少。
若c=6,之后两个正整数a,b,表示询问a所属联通快内所有节点权值之积与b所属联通快内所有节点权值之积的大小,
若a所属联通快内所有节点权值之积大于b所属联通快内所有节点权值之积,输出1,否则为0。
若c=7,之后一个正整数a,表示询问a所在联通块大小
若c=8,之后两个正整数a,b,表示断开a,b所连接的边。
若c=9,之后一个正整数a,表示断开a点的所有连边
具体输出格式见样例
输出
样例输入
1 2
1 3
1 4
1 5
1 6
2 1 2
2 2 3
2 3 4
2 4 5
3 2 5
5 3 4
样例输出
提示
对100%的数据 0<=m<=400000,c<=7,所有出现的数均<=1000000000,所有出现的点保证存在
【HINT】请认真阅读题面
刚读完题面可能会觉得这道题不可做,8、9操作怎么搞?但再往下看看数据范围c<=7,根本不存在后两个操作!
所以原题样例也就修改成了上面的这个样例。这样用线段树合并+并查集就能做了。
我们来分别说说每个操作:
1、直接建一个点,并建一棵这个点所代表的权值线段树(别忘了动态开点哦!)
2、如果这两个点在同一棵联通块中这个操作就没用了,因为询问只询问联通块信息,否则把这两个点所在的联通块合并,并把两个联通块的祖先所代表的权值线段树合并
3、直接找到a联通块祖先的线段树,区间修改就好了,具体见代码。
4、实现同上。
5、还是找到联通块祖先的权值线段树查询第k小。
6、正常思路是维护线段树区间乘积,然后直接查询a,b联通块祖先线段树中根节点的权值乘积就好了,但发现乘积太大了,因此考虑转成log。因为log(x*y)=logx+logy,所以每个点权值取log然后维护区间和即可,精度在double下能过。
7、如果用启发式合并直接输出联通块大小即可,不启发式合并还要在线段树上维护区间数的个数。
#include<set> #include<map> #include<stack> #include<queue> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; int f[400010]; int root[400010]; int cnt; int size[400010]; int ls[7600010]; int rs[7600010]; bool a[7600010]; double g[7600010]; int sum[7600010]; int n; int opt; int x,y; int num; int find(int x) { if(f[x]==x) { return x; } return f[x]=find(f[x]); } void pushup(int rt) { sum[rt]=sum[ls[rt]]+sum[rs[rt]]; g[rt]=g[ls[rt]]+g[rs[rt]]; } void pushdown(int rt) { if(a[rt]) { a[ls[rt]]=1; a[rs[rt]]=1; sum[ls[rt]]=0; g[ls[rt]]=0; sum[rs[rt]]=0; g[rs[rt]]=0; a[rt]=0; } } void insert(int &rt,int l,int r,int k) { if(!rt) { rt=++cnt; } if(l==r) { sum[rt]++; g[rt]+=log(l); return ; } int mid=(l+r)>>1; if(k<=mid) { insert(ls[rt],l,mid,k); } else { insert(rs[rt],mid+1,r,k); } pushup(rt); } void changemin(int &rt,int l,int r,int k,int x) { if(!rt) { rt=++cnt; } if(l==r) { sum[rt]+=x; g[rt]+=log(l)*x; return ; } int mid=(l+r)>>1; pushdown(rt); if(k<=mid) { changemin(ls[rt],l,mid,k,x); } else { x+=sum[ls[rt]];; g[ls[rt]]=0; sum[ls[rt]]=0; a[ls[rt]]=1; changemin(rs[rt],mid+1,r,k,x); } pushup(rt); } void changemax(int &rt,int l,int r,int k,int x) { if(!rt) { rt=++cnt; } if(l==r) { sum[rt]+=x; g[rt]+=log(l)*x; return ; } int mid=(l+r)>>1; pushdown(rt); if(k<=mid) { x+=sum[rs[rt]]; sum[rs[rt]]=0; g[rs[rt]]=0; a[rs[rt]]=1; changemax(ls[rt],l,mid,k,x); } else { changemax(rs[rt],mid+1,r,k,x); } pushup(rt); } int query(int rt,int l,int r,int k) { if(l==r) { return l; } int mid=(l+r)>>1; pushdown(rt); if(sum[ls[rt]]>=k) { return query(ls[rt],l,mid,k); } else { return query(rs[rt],mid+1,r,k-sum[ls[rt]]); } } void merge(int &rt,int x) { if(!rt||!x) { rt=rt+x; return ; } pushdown(rt); pushdown(x); sum[rt]+=sum[x]; g[rt]+=g[x]; merge(ls[rt],ls[x]); merge(rs[rt],rs[x]); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&opt); if(opt==1) { scanf("%d",&x); num++; f[num]=num; size[num]=1; insert(root[num],1,1000000000,x); } else if(opt==2) { scanf("%d%d",&x,&y); int fx=find(x); int fy=find(y); if(fx!=fy) { if(size[fx]>size[fy]) { size[fx]+=size[fy]; f[fy]=fx; merge(root[fx],root[fy]); } else { size[fy]+=size[fx]; f[fx]=fy; merge(root[fy],root[fx]); } } } else if(opt==3) { scanf("%d%d",&x,&y); x=find(x); changemin(root[x],1,1000000000,y,0); } else if(opt==4) { scanf("%d%d",&x,&y); x=find(x); changemax(root[x],1,1000000000,y,0); } else if(opt==5) { scanf("%d%d",&x,&y); x=find(x); printf("%d\n",query(root[x],1,1000000000,y)); } else if(opt==6) { scanf("%d%d",&x,&y); x=find(x); y=find(y); if(g[root[x]]>g[root[y]]) { printf("1\n"); } else { printf("0\n"); } } else if(opt==7) { scanf("%d",&x); x=find(x); printf("%d\n",size[x]); } } }