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]);
}
}
}
BZOJ4399魔法少女LJJ——线段树合并+并查集的更多相关文章
- bzoj4399 魔法少女LJJ 线段树合并
只看题面绝对做不出系列.... 注意到\(c \leqslant 7\),因此不会有删边操作(那样例删边干嘛) 注意到\(2, 5\)操作十分的有趣,启示我们拿线段树合并来做 操作\(7\)很好处理 ...
- bzoj4399 魔法少女LJJ 线段树合并+线段树二分+并查集
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4399 题解 毒瘤题 \(9\) 种操作还有支持动态图的连通性 仔细读题 $ c<=7$. ...
- 【BZOJ4399】魔法少女LJJ 线段树合并
[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的 ...
- BZOJ 4399: 魔法少女LJJ 线段树合并 + 对数
Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着 ...
- BZOJ.4399.魔法少女LJJ(线段树合并)
BZOJ 注意\(c\leq7\)→_→ 然后就是裸的权值线段树+线段树合并了. 对于取\(\max/\min\)操作可以直接区间修改清空超出范围的值,然后更新到对应位置上就行了(比如对\(v\)取\ ...
- Educational Codeforces Round 51 (Rated for Div. 2) G. Distinctification(线段树合并 + 并查集)
题意 给出一个长度为 \(n\) 序列 , 每个位置有 \(a_i , b_i\) 两个参数 , \(b_i\) 互不相同 ,你可以进行任意次如下的两种操作 : 若存在 \(j \not = i\) ...
- 魔法少女 LJJ——线段树
题目 [题目描述] 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女 LJJ 已经觉得自己见过世界上的所有稀奇古怪的事情了. LJJ 感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处 ...
- BZOJ 4399: 魔法少女LJJ(线段树)
传送门 解题思路 出题人真会玩..操作\(2\)线段树合并,然后每棵线段树维护元素个数和.对于\(6\)这个询问,因为乘积太大,所以要用对数.时间复杂度\(O(nlogn)\) 代码 #include ...
- Codeforces.1051G.Distinctification(线段树合并 并查集)
题目链接 \(Description\) 给定\(n\)个数对\(A_i,B_i\).你可以进行任意次以下两种操作: 选择一个位置\(i\),令\(A_i=A_i+1\),花费\(B_i\).必须存在 ...
随机推荐
- omcat+java的web程序持续占cpu高问题调试【转】
1.top -c 2.查看具体线程 ps -m -p 30997 -o tid,%cpu,%mem > threads.log 3.printf %x 31865 其次将需要的线程ID转换为16 ...
- 办公室的远程传文件 的命令三种方式linux
不同的Linux之间copy文件常用有3种方法: 第一种就是ftp,也就是其中一台Linux安装ftp Server,这样可以另外一台使用ftp的client程序来进行文件的copy. 第二种方法就是 ...
- Matplotlib 简单图例
图例参考:http://matplotlib.org/gallery.html API参考:http://matplotlib.org/api/pyplot_summary.html # -*- co ...
- Python高级特性(切片,迭代,列表生成式,生成器,迭代器)
掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...
- Nagios监控系统部署(源码)
1. 概述2. 部署Nagios2.1 创建Nagios用户组2.2 下载Nagios和Nagios-plugin源码2.3 编译安装3. 部署Nagios-plugin3.1 编译安装nagios- ...
- asp.net mvc接收安卓post的json字符串
筛选器: using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syst ...
- PyCharm Tips 常用操作帮助
以下内容转自 http://www.2cto.com/os/201410/341542.html --------------------------------------------------- ...
- required: true,el-upload :action="UploadUrl()"
<el-form-item label="所属班级:" prop="Name" :rules="[{ required: true, messa ...
- Visual Studio的安装与单元测试
一.Visual Studio的安装 由于上学期重装了win10系统,以前使用的vc++6.0不能够正常使用,所以直接就安装了Visual Studio 2015,安装的时候就直接按照提示的步骤进行安 ...
- M1/M2阶段总结
之前提问的博客 问题解答 问题 1 关于代码复审,复审者是否应该参与编码?如果复审者也参与编码的话,那么难免任务量较多,但如果不参与编码的话,工作分配的似乎不太均衡. 我们的团队项目在M1和M2阶段没 ...