题目大意:
一个动态图,支持以下$7$种操作:
1.加入一个权值为$x$的点;
2.在点$a$和点$b$之间加入一条无向边;
3.在点$a$所属的连通块中将小于$x$的所有权值修改为$x$;
4.在点$a$所属的连通块中将大于$x$的所有权值修改为$x$;
5.查找点$a$所属的连通块中第$k$小的权值;
6.比较点$a$所属连通块中所有权值之积与点$b$所属连通块中所有权值之积的大小;
7.询问点$a$所属的连通块的大小。

思路:
建立一棵动态开点的权值线段树,支持以下$7$种操作:
1.插入一个点(新建一棵树);
2.合并两棵树(并查集);
3.询问$[1,x-1]$的元素个数,单点修改加到$x$上,并区间修改删除$[1,x-1]$的权值;
4.询问$[x+1,n]$的元素个数,单点修改加到$x$上,并区间修改删除$[x+1,n]$的权值;
5.查找全树第$k$最值;
6.对于线段树每个结点维护$\log$值,因为$\ln(a\times b)=\ln(a)+\ln(b)$,所以每次加起来比较和就行了;
7.返回整棵树的元素个数。
另外因为数据范围比较大需要离散化,要事先将整个文件读进来。

玄学问题:
1.区间修改打lazy tag似乎和暴力修改差不多,甚至会更慢;
2.似乎很容易被卡内存,因此同样的数据尽量只存一次,离散化宁可每次用std::lower_bound()。

低级错误:
1.$\log$维护老的权值,而不是离散化以后的新权值;
2.先进行线段树合并再并查集合并,不然并查集Find()的时候回返回同一个点;
3.离散化的时候vector是从$0$开始存的,因此最后通过离散的权值求原来的权值,应该是$w[b-1]$而不是$w[b]$;
4.单点修改的时候,原来的$val$值不一定是$1$。

优化:
1.在进行3、4操作时,如果查找到对应区间的元素个数是$0$,那么后面两个操作可以省掉;
2.在区间查询query(),区间修改删除clear()时,如果当前区间的元素个数是$0$,那么不需要再往下递归。

  1 #include<cmath>
  2 #include<cstdio>
  3 #include<vector>
  4 #include<bits/stl_algo.h>
  5 #include<bits/localefwd.h>
  6 inline int getint() {
  7     char ch;
  8     while(!isdigit(ch=getchar()));
  9     int x=ch^'0';
 10     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 11     return x;
 12 }
 13 const int M=400001,logM=17;
 14 int data[M][3];
 15 std::vector<int> w;
 16 class DisjointSet {
 17     private:
 18         int anc[M];
 19     public:
 20         DisjointSet() {
 21             for(int i=0;i<M;i++) anc[i]=i;
 22         }
 23         int Find(const int x) {
 24             return x==anc[x]?x:anc[x]=Find(anc[x]);
 25         }
 26         void Union(const int x,const int y) {
 27             anc[Find(x)]=Find(y);
 28         }
 29         bool isConnected(const int x,const int y) {
 30             return Find(x)==Find(y);
 31         }
 32 };
 33 DisjointSet s;
 34 class SegmentTree {
 35     private:
 36         static const int nil=0;
 37         static const int SIZE=M*logM;
 38         int val[SIZE],left[SIZE],right[SIZE];
 39         double sum[SIZE];
 40         bool tag[SIZE];
 41         int sz;
 42         int newnode() {
 43             return ++sz;
 44         }
 45         void push_up(const int p) {
 46             val[p]=val[left[p]]+val[right[p]];
 47             sum[p]=sum[left[p]]+sum[right[p]];
 48         }
 49         void push_down(const int p) {
 50             if(!tag[p]) return;
 51             tag[p]=false;
 52             tag[left[p]]=tag[right[p]]=true;
 53             val[left[p]]=val[right[p]]=0;
 54             sum[left[p]]=sum[right[p]]=0;
 55         }
 56     public:
 57         int root[M];
 58         SegmentTree() {
 59             sz=0;
 60             val[nil]=0;
 61             sum[nil]=0;
 62             tag[nil]=false;
 63         }
 64         void insert(int &p,const int b,const int e,const int x) {
 65             p=newnode();
 66             left[p]=right[p]=nil;
 67             tag[p]=false;
 68             if(b==e) {
 69                 val[p]=1;
 70                 sum[p]=log(w[x-1]);
 71                 return;
 72             }
 73             int mid=(b+e)>>1;
 74             if(x<=mid) insert(left[p],b,mid,x);
 75             if(x>mid) insert(right[p],mid+1,e,x);
 76             push_up(p);
 77         }
 78         int merge(const int p1,const int p2) {
 79             if(!p1||!p2) return p1|p2;
 80             push_down(p1);
 81             push_down(p2);
 82             val[p1]+=val[p2];
 83             sum[p1]+=sum[p2];
 84             left[p1]=merge(left[p1],left[p2]);
 85             right[p1]=merge(right[p1],right[p2]);
 86             return p1;
 87         }
 88         void modify(int &p,const int b,const int e,const int x,const int y) {
 89             if(!p) p=newnode();
 90             if(b==e) {
 91                 val[p]+=y;
 92                 sum[p]=log(w[b-1])*val[p];
 93                 return;
 94             }
 95             push_down(p);
 96             int mid=(b+e)>>1;
 97             if(x<=mid) modify(left[p],b,mid,x,y);
 98             if(x>mid) modify(right[p],mid+1,e,x,y);
 99             push_up(p);
100         }
101         int query(const int p,const int b,const int e,const int l,const int r) {
102             if(!val[p]) return 0;
103             if((b==l)&&(e==r)) return val[p];
104             push_down(p);
105             int mid=(b+e)>>1,ret=0;
106             if(l<=mid) ret+=query(left[p],b,mid,l,std::min(mid,r));
107             if(r>mid) ret+=query(right[p],mid+1,e,std::max(mid+1,l),r);
108             return ret;
109         }
110         void clear(const int p,const int b,const int e,const int l,const int r) {
111             if(!val[p]) return;
112             if((b==l)&&(e==r)) {
113                 val[p]=0;
114                 sum[p]=0;
115                 tag[p]=true;
116             }
117             push_down(p);
118             int mid=(b+e)>>1;
119             if(l<=mid) clear(left[p],b,mid,l,std::min(mid,r));
120             if(r>mid) clear(right[p],mid+1,e,std::max(mid+1,l),r);
121             push_up(p);
122         }
123         void modify_less(int &p,const int b,const int e,const int x) {
124             int d=query(p,b,e,b,x-1);
125             if(!d) return;
126             modify(p,b,e,x,d);
127             clear(p,b,e,b,x-1);
128         }
129         void modify_greater(int &p,const int b,const int e,const int x) {
130             int d=query(p,b,e,x+1,e);
131             if(!d) return;
132             modify(p,b,e,x,d);
133             clear(p,b,e,x+1,e);
134         }
135         int find_kth(const int p,const int b,const int e,const int k) {
136             if(b==e) return w[b-1];
137             push_down(p);
138             int mid=(b+e)>>1;
139             if(val[left[p]]>=k) return find_kth(left[p],b,mid,k);
140             return find_kth(right[p],mid+1,e,k-val[left[p]]);
141         }
142         bool cmp_product(const int x,const int y) {
143             return sum[x]>sum[y];
144         }
145         int size(const int x) {
146             return val[x];
147         }
148 };
149 SegmentTree t;
150 int main() {
151     int m=getint();
152     for(int i=0;i<m;i++) {
153         int &c=data[i][0]=getint();
154         data[i][1]=getint();
155         if(c!=1&&c!=7) data[i][2]=getint();
156         if(c==1) w.push_back(data[i][1]);
157         if(c==3||c==4) w.push_back(data[i][2]);
158     }
159     std::sort(w.begin(),w.end());
160     int n=std::unique(w.begin(),w.end())-w.begin();
161     w.resize(n);
162     for(int i=0,cnt=0;i<m;i++) {
163         int &c=data[i][0];
164         switch(c) {
165             case 1: {
166                 cnt++;
167                 int x=std::lower_bound(w.begin(),w.end(),data[i][1])-w.begin()+1;
168                 t.insert(t.root[cnt],1,n,x);
169                 break;
170             }
171             case 2: {
172                 int &a=data[i][1],&b=data[i][2];
173                 if(s.isConnected(a,b)) continue;
174                 t.root[s.Find(b)]=t.merge(t.root[s.Find(b)],t.root[s.Find(a)]);
175                 s.Union(a,b);
176                 break;
177             }
178             case 3: {
179                 int &a=data[i][1],x=lower_bound(w.begin(),w.end(),data[i][2])-w.begin()+1;
180                 t.modify_less(t.root[s.Find(a)],1,n,x);
181                 break;
182             }
183             case 4: {
184                 int &a=data[i][1],x=lower_bound(w.begin(),w.end(),data[i][2])-w.begin()+1;
185                 t.modify_greater(t.root[s.Find(a)],1,n,x);
186                 break;
187             }
188             case 5: {
189                 int &a=data[i][1],&k=data[i][2];
190                 printf("%d\n",t.find_kth(t.root[s.Find(a)],1,n,k));
191                 break;
192             }
193             case 6: {
194                 int &a=data[i][1],&b=data[i][2];
195                 printf("%d\n",t.cmp_product(t.root[s.Find(a)],t.root[s.Find(b)]));
196                 break;
197             }
198             case 7: {
199                 int &a=data[i][1];
200                 printf("%d\n",t.size(t.root[s.Find(a)]));
201                 break;
202             }
203         }
204     }
205     return 0;
206 }

转载于:https://www.cnblogs.com/skylee03/p/7448212.html

[BZOJ4399]魔法少女LJJ相关推荐

  1. bzoj4399 魔法少女LJJ

    这题不愧为4399. 4399: 魔法少女LJJ Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 622  Solved: 145 [Submit][ ...

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

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

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

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

  4. [BZOJ4399]魔法少女LJJ(线段树合并)

    请仔细阅读数据范围,c<=7. 线段树合并裸题,对于乘积大小比较,使用log即可. 1 #include<cmath> 2 #include<cstdio> 3 #inc ...

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

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

  6. 【bzoj4399】魔法少女LJJ 并查集+权值线段树合并

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

  7. BZOJ 4399: 魔法少女LJJ

    4399: 魔法少女LJJ Time Limit: 20 Sec Memory Limit: 162 MB Submit: 287 Solved: 73 Description 在森林中见过会动的树, ...

  8. bzoj 4399 魔法少女LJJ

    4399: 魔法少女LJJ Time Limit: 20 Sec  Memory Limit: 162 MB http://www.lydsy.com/JudgeOnline/problem.php? ...

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

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

最新文章

  1. Android 中文 API 文档 (45) —— AbsoluteLayout.LayoutParams
  2. 生成删除约束语句 SQL语句
  3. printf 指针地址_c语言入门 第十四章指针
  4. Javascript 笔记与总结(1-4)this
  5. google us web
  6. 并发、并行、线程、进程与CPU基本概念
  7. 网络分析仪-inSSIDer
  8. c语言编写弹窗提示是否确认退出_弹窗设计的5条基本原则
  9. return view前端怎么获取_Web 前端路由原理解析和功能实现
  10. 前端面试要注意这几点
  11. 漏洞战争: 软件漏洞发展趋势
  12. OpenStack DVR 原理深入分析
  13. 【数据仓库与OLAP技术】期末复习+考题
  14. Java中文件File相关知识
  15. 计算机自动关机启机唤醒设置,电脑在哪设置定时关机(如何设置电脑的自动关机和自动开机)...
  16. WhereDidMyTimeGo - 一款帮你记录每天的时间分配的MacOS app
  17. pushplus通过企业微信应用给微信发送消息教程
  18. cpu怎么开启php,cpu怎么换
  19. java编写水数仙花,【360高手联盟-小仙】【JAVASE编程基础】02-JAVA程序的基本结构和基础语法...
  20. 基于NIO的Client/Server程序实践

热门文章

  1. 零基础Java学习之final关键字
  2. 2018年这些UI设计趋势正在流行,跟上必拿高薪!
  3. stopImmediatePropagation函数和stopPropagation函数的区别
  4. 【leetcode】Balanced Binary Tree(middle)
  5. ASP.NET那点不为人知的事(四)
  6. 用SAXBuilder、Document、Element操作xml
  7. GridView 里的删除不起作用
  8. OpenCv 金字塔之上采样与下采样
  9. springboot中下面哪一个作为jpa默认实现_天天在用SpringBoot,手撸一个的Starter试试!...
  10. python 驱动级鼠标_Python介绍、安装