感谢线段树进阶,给了我重新做人的机会。---------------某不知名OIer,Keen_z

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很郁闷,你能帮帮他吗

输入格式

第一行有一个正整数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点的所有连边 具体输出格式见样例

HINT

对100%的数据 0<=m<=400000,c<=7,所有出现的数均<=1000000000,所有出现的点保证存在 【HINT】请认真阅读题面

Solution

给了8,9两个阴间删点删边操作之后告诉你c<=7。。

一个很喵的转化,可以将连乘转化为对数之和。

数据最大1e9,显然不能直接开线段树,需要离散化。

将每个联通块都看作一个线段树,在其中维护权值为i的点的个数与元素对数的和。连边时将两点所在的线段树合并。

过程中要用一波并察集操作,来及时更新合并后线段树的根。

主要还是看代码吧,应该挺好理解的(挺不好调的

附去掉9 1后的样例输入,输出应为5。


code:

#include<bits/stdc++.h>
#define debug cout<<"wrong"<<endl
using namespace std;
const int NN=4e5+5;
int hal,id,c[NN],rt[NN],m,dat[NN][3],has[NN],cnt,fa[NN],s;
double logg[NN];
inline signed read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
void write(int x){
if(x<0) putchar('-'), x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int get(int x){
return fa[x]==x?x:fa[x]=get(fa[x]);
}
inline int cag(int x){
return lower_bound(has+1,has+1+hal,x)-has;
}
struct node{
int seg,ls[NN*20],rs[NN*20],caf[NN*20],siz[NN*20];
double sum[NN*20];
void pushup(int x){
sum[x]=sum[ls[x]]+sum[rs[x]];
siz[x]=siz[ls[x]]+siz[rs[x]];
}
void insert(int &x,int l,int r,int pos,int w){
if(!x) x=++seg;
if(l==r){
sum[x]+=logg[pos]*w;
siz[x]+=w;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(ls[x],l,mid,pos,w);
else insert(rs[x],mid+1,r,pos,w);
pushup(x);
}
int query(int x,int l,int r,int pos){
if(l==r) return l;
int mid=(l+r)>>1;
if(siz[ls[x]]>=pos) return query(ls[x],l,mid,pos);
else return query(rs[x],mid+1,r,pos-siz[ls[x]]);
}
void marge(int &x,int y,int l,int r){
if(!x||!y){
x=x+y;
return;
}
if(l==r){
siz[x]+=siz[y];
sum[x]+=sum[y];
return;
}
int mid=(l+r)>>1;
marge(ls[x],ls[y],l,mid);
marge(rs[x],rs[y],mid+1,r);
pushup(x);
}
void delet(int x,int l,int r,int opl,int opr){
if(!siz[x]) return;
if(l==r){
s+=siz[x];
sum[x]=siz[x]=0;
return;
}
int mid=(l+r)>>1;
if(opl<=mid) delet(ls[x],l,mid,opl,opr);
if(opr>mid) delet(rs[x],mid+1,r,opl,opr);
pushup(x);
}
}segt;
int main(){
m=read();
for(int i=1;i<=m;i++){
c[i]=read(); dat[i][1]=read();
if(c[i]!=1&&c[i]!=7) dat[i][2]=read();
if(c[i]==1) has[++cnt]=dat[i][1], dat[i][2]=++id;
if(c[i]==3||c[i]==4) has[++cnt]=dat[i][2];
}
sort(has+1,has+cnt+1);
hal=unique(has+1,has+cnt+1)-(has+1);
for(int i=1;i<=m;i++)
switch(c[i]){
case 1:{
int x=cag(dat[i][1]);
logg[x]=log(dat[i][1]);
segt.insert(rt[dat[i][2]],1,hal,x,1);
fa[dat[i][2]]=dat[i][2];
break;
}
case 2:{
int rx=get(dat[i][1]),ry=get(dat[i][2]);
if(rx!=ry){
fa[ry]=rx;
segt.marge(rt[rx],rt[ry],1,hal);
}
break;
}
case 3:{
int x=cag(dat[i][2]),ro=get(dat[i][1]);
s=0; logg[x]=log(dat[i][2]);
segt.delet(rt[ro],1,hal,1,max(1,x-1));
if(s) segt.insert(rt[ro],1,hal,x,s);
break;
}
case 4:{
int x=cag(dat[i][2]),ro=get(dat[i][1]);
s=0; logg[x]=log(dat[i][2]);
segt.delet(rt[ro],1,hal,min(hal,x+1),hal);
if(s) segt.insert(rt[ro],1,hal,x,s);
break;
}
case 5:{
int ro=get(dat[i][1]);
int ans=segt.query(rt[ro],1,hal,dat[i][2]);
write(has[ans]); putchar('\n');
break;
}
case 6:{
int r1=get(dat[i][1]),r2=get(dat[i][2]);
if(segt.sum[rt[r1]]>segt.sum[rt[r2]]) puts("1");
else puts("0");
break;
}
case 7:{
int ro=get(dat[i][1]);
write(segt.siz[rt[ro]]); putchar('\n');
break;
}
}
}
/*
11
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
*/

Code

  1 #include<bits/stdc++.h>
2 #define debug cout<<"lbwnb"<<endl
3 using namespace std;
4 const int NN=4e5+5;
5 int hal,id,c[NN],rt[NN],m,dat[NN][3],has[NN],cnt,fa[NN],s;
6 double logg[NN];
7 inline signed read(){
8 int x=0,f=1;
9 char ch=getchar();
10 while(ch<'0'||ch>'9')
11 {
12 if(ch=='-') f=-1;
13 ch=getchar();
14 }
15 while(ch<='9'&&ch>='0')
16 {
17 x=(x<<1)+(x<<3)+(ch^48);
18 ch=getchar();
19 }
20 return x*f;
21 }
22 void write(int x){
23 if(x<0) putchar('-'), x=-x;
24 if(x>9) write(x/10);
25 putchar(x%10+'0');
26 }
27 int get(int x){
28 return fa[x]==x?x:fa[x]=get(fa[x]);
29 }
30 inline int cag(int x){
31 return lower_bound(has+1,has+1+hal,x)-has;
32 }
33 struct node{
34 int seg,ls[NN*20],rs[NN*20],caf[NN*20],siz[NN*20];
35 double sum[NN*20];
36 void pushup(int x){
37 sum[x]=sum[ls[x]]+sum[rs[x]];
38 siz[x]=siz[ls[x]]+siz[rs[x]];
39 }
40 void insert(int &x,int l,int r,int pos,int w){
41 if(!x) x=++seg;
42 if(l==r){
43 sum[x]+=logg[pos]*w;
44 siz[x]+=w;
45 return;
46 }
47 int mid=(l+r)>>1;
48 if(pos<=mid) insert(ls[x],l,mid,pos,w);
49 else insert(rs[x],mid+1,r,pos,w);
50 pushup(x);
51 }
52 int query(int x,int l,int r,int pos){
53 if(l==r) return l;
54 int mid=(l+r)>>1;
55 if(siz[ls[x]]>=pos) return query(ls[x],l,mid,pos);
56 else return query(rs[x],mid+1,r,pos-siz[ls[x]]);
57 }
58 void marge(int &x,int y,int l,int r){
59 if(!x||!y){
60 x=x+y;
61 return;
62 }
63 if(l==r){
64 siz[x]+=siz[y];
65 sum[x]+=sum[y];
66 return;
67 }
68 int mid=(l+r)>>1;
69 marge(ls[x],ls[y],l,mid);
70 marge(rs[x],rs[y],mid+1,r);
71 pushup(x);
72 }
73 void delet(int x,int l,int r,int opl,int opr){
74 if(!siz[x]) return;
75 if(l==r){
76 s+=siz[x];
77 sum[x]=siz[x]=0;
78 return;
79 }
80 int mid=(l+r)>>1;
81 if(opl<=mid) delet(ls[x],l,mid,opl,opr);
82 if(opr>mid) delet(rs[x],mid+1,r,opl,opr);
83 pushup(x);
84 }
85 }segt;
86 int main(){
87 m=read();
88 for(int i=1;i<=m;i++){
89 c[i]=read(); dat[i][1]=read();
90 if(c[i]!=1&&c[i]!=7) dat[i][2]=read();
91 if(c[i]==1) has[++cnt]=dat[i][1], dat[i][2]=++id;
92 if(c[i]==3||c[i]==4) has[++cnt]=dat[i][2];
93 }
94 sort(has+1,has+cnt+1);
95 hal=unique(has+1,has+cnt+1)-(has+1);
96 for(int i=1;i<=m;i++)
97 switch(c[i]){
98 case 1:{
99 int x=cag(dat[i][1]);
100 logg[x]=log(dat[i][1]);
101 segt.insert(rt[dat[i][2]],1,hal,x,1);
102 fa[dat[i][2]]=dat[i][2];
103 break;
104 }
105 case 2:{
106 int rx=get(dat[i][1]),ry=get(dat[i][2]);
107 if(rx!=ry){
108 fa[ry]=rx;
109 segt.marge(rt[rx],rt[ry],1,hal);
110 }
111 break;
112 }
113 case 3:{
114 int x=cag(dat[i][2]),ro=get(dat[i][1]);
115 s=0; logg[x]=log(dat[i][2]);
116 segt.delet(rt[ro],1,hal,1,max(1,x-1));
117 if(s) segt.insert(rt[ro],1,hal,x,s);
118 break;
119 }
120 case 4:{
121 int x=cag(dat[i][2]),ro=get(dat[i][1]);
122 s=0; logg[x]=log(dat[i][2]);
123 segt.delet(rt[ro],1,hal,min(hal,x+1),hal);
124 if(s) segt.insert(rt[ro],1,hal,x,s);
125 break;
126 }
127 case 5:{
128 int ro=get(dat[i][1]);
129 int ans=segt.query(rt[ro],1,hal,dat[i][2]);
130 write(has[ans]); putchar('\n');
131 break;
132 }
133 case 6:{
134 int r1=get(dat[i][1]),r2=get(dat[i][2]);
135 if(segt.sum[rt[r1]]>segt.sum[rt[r2]]) puts("1");
136 else puts("0");
137 break;
138 }
139 case 7:{
140 int ro=get(dat[i][1]);
141 write(segt.siz[rt[ro]]); putchar('\n');
142 break;
143 }
144 }
145 }
146 /*
147 11
148 1 2
149 1 3
150 1 4
151 1 5
152 1 6
153 2 1 2
154 2 2 3
155 2 3 4
156 2 4 5
157 3 2 5
158 5 3 4

[BZOJ4399]魔法少女LJJ----------线段树进阶的更多相关文章

  1. BZOJ4399魔法少女LJJ——线段树合并+并查集

    题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...

  2. bzoj4399 魔法少女LJJ 线段树合并

    只看题面绝对做不出系列.... 注意到\(c \leqslant 7\),因此不会有删边操作(那样例删边干嘛) 注意到\(2, 5\)操作十分的有趣,启示我们拿线段树合并来做 操作\(7\)很好处理 ...

  3. bzoj4399 魔法少女LJJ 线段树合并+线段树二分+并查集

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4399 题解 毒瘤题 \(9\) 种操作还有支持动态图的连通性 仔细读题 $ c<=7$. ...

  4. 【BZOJ4399】魔法少女LJJ 线段树合并

    [BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的 ...

  5. 魔法少女 LJJ——线段树

    题目 [题目描述] 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女 LJJ 已经觉得自己见过世界上的所有稀奇古怪的事情了. LJJ 感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处 ...

  6. BZOJ 4399: 魔法少女LJJ 线段树合并 + 对数

    Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着 ...

  7. BZOJ.4399.魔法少女LJJ(线段树合并)

    BZOJ 注意\(c\leq7\)→_→ 然后就是裸的权值线段树+线段树合并了. 对于取\(\max/\min\)操作可以直接区间修改清空超出范围的值,然后更新到对应位置上就行了(比如对\(v\)取\ ...

  8. BZOJ 4399: 魔法少女LJJ(线段树)

    传送门 解题思路 出题人真会玩..操作\(2\)线段树合并,然后每棵线段树维护元素个数和.对于\(6\)这个询问,因为乘积太大,所以要用对数.时间复杂度\(O(nlogn)\) 代码 #include ...

  9. BZOJ4399 魔法少女LJJ【线段树合并】【并查集】

    Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了 LJJ感叹道"这里真是个迷人的绿色世界,空气清新.淡雅 ...

随机推荐

  1. list使用详解

    List双向链表 再谈链表 List链表的概念再度出现了,作为线性表的一员,C++的STL提供了快速进行构建的方法,为此,在前文的基础上通过STL进行直接使用,这对于程序设计中快速构建原型是相当有必要 ...

  2. 网络协议之:WebSocket的消息格式

    目录 简介 WebSocket的握手流程 webSocket的消息格式 Extensions和Subprotocols 总结 简介 我们知道WebSocket是建立在TCP协议基础上的一种网络协议,用 ...

  3. Java集合框架总览

    Java集合 Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射.Collection 接口有 3 种子类型,Lis ...

  4. django forms的常用命令及方法(一)

    根据别人网上发布,个人爱好收集 Form表单的功能 自动生成HTML表单元素 检查表单数据的合法性 如果验证错误,重新显示表单(数据不会重置) 数据类型转换(字符类型的数据转换成相应的Python类型 ...

  5. 『Python』matplotlib坐标轴应用

    1. 设置坐标轴的位置和展示形式 import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl mpl.use ...

  6. P4001-[ICPC-Beijing 2006]狼抓兔子【对偶图】

    正题 题目链接:https://www.luogu.com.cn/problem/P4001 题目大意 给出一个类似于 的网格图,求起点到终点的最小割. 解题思路 最小割直接跑网络流,然后发现\(di ...

  7. Java——this关键字总结

    一.this关键字的使用 1.当同类型的对象a和b,调用相同的方法method()时,为了区分该方法是被a调用还是被b调用 如下代码中,: class Banana { void method(int ...

  8. 解决Vue项目打包之后放到nginx下刷新就报错404的问题

    最近跟着某机构的教学视频敲了一遍vue项目,但是在windows环境下部署的时候就懵逼了放到nginx下正常跑没问题,但是刷新之后就报404错误 前端项目构建vue 脚手架版本 是@vue/cli 4 ...

  9. amber模拟kcl水溶液

    最近刚开始学习amber软件,看网上的教程勉强知道怎么操作这个amber了.就暂时跑了个分子动力学,其他的啥也没处理.先把我的操作过程记录下来吧,免得日后忘记. 一.构建kcl.pdb结构 利用Gau ...

  10. Python异常代码含义对照表

    Python常见的异常提示及含义对照表如下: 异常名称 描述 BaseException 所有异常的基类 SystemExit 解释器请求退出 KeyboardInterrupt 用户中断执行(通常是 ...