感谢线段树进阶,给了我重新做人的机会。---------------某不知名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. docker部署rabbitmq集群

    docker版本:18.09.6 或以上 rabbitmq镜像版本:rabbitmq:3.9.5-management 一.拉镜像: docker pull rabbitmq:3.9.5-manage ...

  2. 如何在win10中Java中JDK的安装和path,classpath的环境配置

    1,第一步,不用说肯定是去下一个java JDK了.目前最新版本的java JDK应该是JDK 7.0,这个就自己去百度一下了,好多网站都可以找到.2,第二步就是安装JDK虚拟机了,按照它里面的提示一 ...

  3. elementUI 表格 table 的表头错乱问题

    页面中多组件开发时,如果页面中有表格的,table表格头出现表头错乱 // 全局设置1 body .el-table th.gutter{ 2 display: table-cell!importan ...

  4. Anaconda配置国内镜像源

    1. 为conda配置(清华)镜像源 使用conda进行安装时,访问的是国外的网络,所以下载和安装包时会特别慢.我们需要更换到国内镜像源地址,这里我更换到国内的清华大学地址.(永久添加镜像) Wind ...

  5. POJ1741——Tree(树的点分治)

    1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-11-17 1 ...

  6. POJ题目 1003Hangover(叠放纸牌)

    POJ 1003 叠放纸牌 描述 您可以将多张纸牌悬在桌子上多远?如果您有一张卡,则可以创建一个最大长度为卡长的一半.(我们假设这些卡片必须垂直于桌子.)使用两张卡片,您可以使最上面的卡片悬垂在底部的 ...

  7. git实战-linux定时监控github更新状态(二)

    系列文章 git介绍-常用操作(一)✓ git实战-linux定时监控github更新状态(二)✓ 本文主要内容 如何查看github的本地仓库和远程仓库的同步情况 linux服务器定时监控githu ...

  8. 【PHP数据结构】线性查找与二分查找

    欢迎来到查找的世界,在学习完各种数据结构之后,总算走到了这一步,不知道大家有什么感想呢?反正我是边学边忘,现在让我去说说图的那几个算法还是在蒙圈的状态中.不过学习嘛,就是一步一步的来,暂时搞不懂的东西 ...

  9. Docker系列(22)- DockerFile指令说明并构建自己的centos

    DockerFile常用指令 实战测试 DockerHub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建 #创建一个自己的centos # 1.编写D ...

  10. Android Kotlin协程入门

    Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...