[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 }