3.1 并查集
//带路径压缩的并查集,用于动态维护查询等价类 //图论算法中动态判点集连通常用 //维护和查询复杂度略大于 O(1) //集合元素取值 1..MAXN-1(注意 0 不能用!),默认不等价 #include <string.h> #define MAXN 100000 #define _ufind_run(x) for(;p[t=x];x=p[x],p[t]=(p[x]?p[x]:x)) #define _run_both _ufind_run(i);_ufind_run(j) struct ufind{ int p[MAXN],t; void init(){memset(p,0,sizeof(p));} void set_friend(int i,int j){_run_both;p[i]=(i==j?0:j);} int is_friend(int i,int j){_run_both;return i==j&&i;} }; //带路径压缩的并查集扩展形式 //用于动态维护查询 friend-enemy 型等价类 //维护和查询复杂度略大于 O(1) //集合元素取值 1..MAXN-1(注意 0 不能用!),默认无关 #include <string.h> #define MAXN 100000 #define sig(x) ((x)>0?1:-1) 59 #define abs(x) ((x)>0?(x):-(x)) #define _ufind_run(x) for(;p[t=abs(x)];x=sig(x)*p[abs(x)],p[t]=sig(p[t])*(p[abs(x)]?p[abs(x)]:abs(p[t]))) #define _run_both _ufind_run(i);_ufind_run(j) #define _set_side(x) p[abs(i)]=sig(i)*(abs(i)==abs(j)?0:(x)*j) #define _judge_side(x) (i==(x)*j&&i) struct ufind{ int p[MAXN],t; void init(){memset(p,0,sizeof(p));} int set_friend(int i,int j){_run_both;_set_side(1);return !_judge_side(-1);} int set_enemy(int i,int j){_run_both;_set_side(-1);return !_judge_side(1);} int is_friend(int i,int j){_run_both;return _judge_side(1);} int is_enemy(int i,int j){_run_both;return _judge_side(-1);} };
3.2 堆
//二分堆(binary) //可插入,获取并删除最小(最大)元素,复杂度均 O(logn) //可更改元素类型,修改比较符号或换成比较函数 #define MAXN 10000 #define _cp(a,b) ((a)<(b)) typedef int elem_t; struct heap{ elem_t h[MAXN]; int n,p,c; void init(){n=0;} void ins(elem_t e){ for (p=++n;p>1&&_cp(e,h[p>>1]);h[p]=h[p>>1],p>>=1); h[p]=e; } int del(elem_t& e){ if (!n) return 0; for (e=h[p=1],c=2;c1&&_cp(h[c+1],h[c]))],h[n]);h[p]=h[c],p=c,c<<=1); h[p]=h[n--];return 1; } }; //映射二分堆(mapped) //可插入,获取并删除任意元素,复杂度均 O(logn) //插入时提供一个索引值,删除时按该索引删除,获取并删除最小元素时一起获得该索引 //索引值范围 0..MAXN-1,不能重复,不负责维护索引的唯一性,不在此返回请另外映射 //主要用于图论算法,该索引值可以是节点的下标 //可更改元素类型,修改比较符号或换成比较函数 #define MAXN 10000 #define _cp(a,b) ((a)<(b)) typedef int elem_t; struct heap{ elem_t h[MAXN]; int ind[MAXN],map[MAXN],n,p,c; void init(){n=0;} void ins(int i,elem_t e){ for (p=++n;p>1&&_cp(e,h[p>>1]);h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1); h[map[ind[p]=i]=p]=e; } int del(int i,elem_t& e){ i=map[i];if (i<1||i>n) return 0; for (e=h[p=i];p>1;h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1); for (c=2;c 1&&_cp(h[c+1],h[c]))],h[n]);h[map[ind[p]=ind[c]]=p]=h[c],p=c,c<< =1); h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1; } int delmin(int& i,elem_t& e){ if (n<1) return 0;i=ind[1]; for (e=h[p=1],c=2;c 1&&_cp(h[c+1],h[c]))],h[n]);h[map[ind[p]=ind[c]]=p]=h[c ],p=c,c<<=1); h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1; } };
3.3 线段树
线段树应用:
求面积:
1) 坐标离散化
2) 垂直边按 x 坐标排序
3) 从左往右用线段树处理垂直边
累计每个离散 x 区间长度和线段树长度的乘积
求周长:
1) 坐标离散化
2) 垂直边按 x 坐标排序, 第二关键字为入边优于出边
3) 从左往右用线段树处理垂直边
61
在每个离散点上先加入所有入边, 累计线段树长度变化值
再删除所有出边, 累计线段树长度变化值
4) 水平边按 y 坐标排序, 第二关键字为入边优于出边
5) 从上往下用线段树处理水平边
在每个离散点上先加入所有入边, 累计线段树长度变化值
再删除所有出边, 累计线段树长度变化值
//线段树 //可以处理加入边和删除边不同的情况 //inc_seg 和 dec_seg 用于加入边 //seg_len 求长度 //t 传根节点(一律为 1) //l0,r0 传树的节点范围(一律为 1..t) //l,r 传线段(端点) #define MAXN 10000 struct segtree{ int n,cnt[MAXN],len[MAXN]; segtree(int t):n(t){ for (int i=1;i<=t;i++) cnt[i]=len[i]=0; }; void update(int t,int l,int r); void inc_seg(int t,int l0,int r0,int l,int r); void dec_seg(int t,int l0,int r0,int l,int r); int seg_len(int t,int l0,int r0,int l,int r); }; int length(int l,int r){ return r-l; } void segtree::update(int t,int l,int r){ if (cnt[t]||r-l==1) len[t]=length(l,r); else len[t]=len[t+t]+len[t+t+1]; } void segtree::inc_seg(int t,int l0,int r0,int l,int r){ if (l0==l&&r0==r) cnt[t]++; else{ int m0=(l0+r0)>>1; if (l<m0) inc_seg(t+t,l0,m0,l,m0m0:r); if (r>m0) inc_seg(t+t+1,m0,r0,m0>l?m0:l,r); if (cnt[t+t]&&cnt[t+t+1]){ cnt[t+t]--; update(t+t,l0,m0); cnt[t+t+1]--; update(t+t+1,m0,r0); cnt[t]++; } } update(t,l0,r0); } void segtree::dec_seg(int t,int l0,int r0,int l,int r){ if (l0==l&&r0==r) cnt[t]--; else if (cnt[t]){ cnt[t]--; if (l>l0) inc_seg(t,l0,r0,l0,l); if (r<r0) inc_seg(t,l0,r0,r,r0); } else{ int m0=(l0+r0)>>1; if (l<m0) dec_seg(t+t,l0,m0,l,m0 m0:r); if (r>m0) dec_seg(t+t+1,m0,r0,m0>l?m0:l,r); } update(t,l0,r0); } int segtree::seg_len(int t,int l0,int r0,int l,int r){ if (cnt[t]||(l0==l&&r0==r)) return len[t]; else{ int m0=(l0+r0)>>1,ret=0; if (l<m0) ret+=seg_len(t+t,l0,m0,l,m0 m0:r); if (r>m0) ret+=seg_len(t+t+1,m0,r0,m0>l?m0:l,r); return ret; } }
//线段树扩展 //可以计算长度和线段数 //可以处理加入边和删除边不同的情况 //inc_seg 和 dec_seg 用于加入边 //seg_len 求长度,seg_cut 求线段数 //t 传根节点(一律为 1) //l0,r0 传树的节点范围(一律为 1..t) //l,r 传线段(端点) #define MAXN 10000 struct segtree{ int n,cnt[MAXN],len[MAXN],cut[MAXN],bl[MAXN],br[MAXN]; segtree(int t):n(t){ for (int i=1;i<=t;i++) cnt[i]=len[i]=cut[i]=bl[i]=br[i]=0; }; void update(int t,int l,int r); void inc_seg(int t,int l0,int r0,int l,int r); void dec_seg(int t,int l0,int r0,int l,int r); int seg_len(int t,int l0,int r0,int l,int r); int seg_cut(int t,int l0,int r0,int l,int r); }; int length(int l,int r){ return r-l; } void segtree::update(int t,int l,int r){ if (cnt[t]||r-l==1) len[t]=length(l,r),cut[t]=bl[t]=br[t]=1; else{ len[t]=len[t+t]+len[t+t+1]; cut[t]=cut[t+t]+cut[t+t+1]; if (br[t+t]&&bl[t+t+1]) cut[t]--; bl[t]=bl[t+t],br[t]=br[t+t+1]; } } void segtree::inc_seg(int t,int l0,int r0,int l,int r){ if (l0==l&&r0==r) cnt[t]++; 64 else{ int m0=(l0+r0)>>1; if (l<m0) inc_seg(t+t,l0,m0,l,m0m0:r); if (r>m0) inc_seg(t+t+1,m0,r0,m0>l?m0:l,r); if (cnt[t+t]&&cnt[t+t+1]){ cnt[t+t]--; update(t+t,l0,m0); cnt[t+t+1]--; update(t+t+1,m0,r0); cnt[t]++; } } update(t,l0,r0); } void segtree::dec_seg(int t,int l0,int r0,int l,int r){ if (l0==l&&r0==r) cnt[t]--; else if (cnt[t]){ cnt[t]--; if (l>l0) inc_seg(t,l0,r0,l0,l); if (r<r0) inc_seg(t,l0,r0,r,r0); } else{ int m0=(l0+r0)>>1; if (l<m0) dec_seg(t+t,l0,m0,l,m0 m0:r); if (r>m0) dec_seg(t+t+1,m0,r0,m0>l?m0:l,r); } update(t,l0,r0); } int segtree::seg_len(int t,int l0,int r0,int l,int r){ if (cnt[t]||(l0==l&&r0==r)) return len[t]; else{ int m0=(l0+r0)>>1,ret=0; if (l<m0) ret+=seg_len(t+t,l0,m0,l,m0 m0:r); 65 if (r>m0) ret+=seg_len(t+t+1,m0,r0,m0>l?m0:l,r); return ret; } } int segtree::seg_cut(int t,int l0,int r0,int l,int r){ if (cnt[t]) return 1; if (l0==l&&r0==r) return cut[t]; else{ int m0=(l0+r0)>>1,ret=0; if (l<m0) ret+=seg_cut(t+t,l0,m0,l,m0 m0:r); if (r>m0) ret+=seg_cut(t+t+1,m0,r0,m0>l?m0:l,r); if (l m0&&br[t+t]&&bl[t+t+1]) ret--; return ret; } }
3.4 子段和
//求 sum{[0..n-1]} //维护和查询复杂度均为 O(logn) //用于动态求子段和,数组内容保存在 sum.a[]中 //可以改成其他数据类型 #include <string.h> #define lowbit(x) ((x)&((x)^((x)-1))) #define MAXN 10000 typedef int elem_t; struct sum{ elem_t a[MAXN],c[MAXN],ret; int n; void init(int i){memset(a,0,sizeof(a));memset(c,0,sizeof(c));n=i;} void update(int i,elem_t v){for (v-=a[i],a[i++]+=v;i<=n;c[i-1]+=v,i+=lowbit(i));} elem_t query(int i){for (ret=0;i;ret+=c[i-1],i^=lowbit(i));return ret;} };
3.5 子阵和
//求 sum{a[0..m-1][0..n-1]} //维护和查询复杂度均为 O(logm*logn) //用于动态求子阵和,数组内容保存在 sum.a[][]中 //可以改成其他数据类型 #include <string.h> #define lowbit(x) ((x)&((x)^((x)-1))) #define MAXN 100 typedef int elem_t; struct sum{ elem_t a[MAXN][MAXN],c[MAXN][MAXN],ret; int m,n,t; void init(int i,int j){memset(a,0,sizeof(a));memset(c,0,sizeof(c));m=i,n=j;} void update(int i,int j,elem_t v){ for (v-=a[i][j],a[i++][j++]+=v,t=j;i<=m;i+=lowbit(i)) for (j=t;j<=n;c[i-1][j-1]+=v,j+=lowbit(j)); } elem_t query(int i,int j){ for (ret=0,t=j;i;i^=lowbit(i)) for (j=t;j;ret+=c[i-1][j-1],j^=lowbit(j)); return ret; } };