[BZOJ4399]魔法少女LJJ

题目大意:
一个动态图,支持以下$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 }

 

posted @ 2017-08-29 14:51  skylee03  阅读(200)  评论(0编辑  收藏  举报