线段树合并&&启发式合并笔记
这俩东西听起来很高端,实际上很好写,应用也很多~
线段树合并
线段树合并,顾名思义,就是建立一棵新的线段树保存原有的两颗线段树的信息。
考虑如何合并,对于一个结点,如果两颗线段树都有此位置的结点,则直接合并两结点的信息(如维护最大值则取max,维护和则相加),然后递归处理左右子树;
若只有一个有,直接返回即可。
这样子做时间复杂度取决于重合节点个数,一次最坏复杂度是$O(nlogn)$,因为满二叉树的结点数是$O(n)$,对每个结点进行处理是$O(logn)$,但是实际应用中需要合并的两颗树重合部分一般较少,所以复杂度可以近似看为$O(logn)$的;
如果用动态开点线段树的话,一次合并只需要合并一条链,所以时间复杂度是$O(操作数\times logn)$的
启发式合并
启发式合并核心思想就一句话:把小集合的合并到大的里。
启发式合并思想可以放到很多数据结构里,链表、线段树、甚至平衡树都可以。
考虑时间复杂度,设总共有$n$个元素,由于每次集合的大小至少翻倍,所以至多会合并$logn$次,总的复杂度就是$O(nlogn)$的(结合线段树合并就是$O(nlog^2n)$的)
下面举几道例题:
【BZOJ1483】【HNOI2009】梦幻布丁
链表+启发式合并,每次换颜色直接合并
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 #include<cmath>
5 using namespace std;
6 int n,m,s,x,y,ans=0,a[1000001],lsh[1000001],head[1000001],nxt[1000001],sum[1000001],pre[1000001];
7 void work(int x,int y){
8 for(int tmp=head[x];tmp!=-1;tmp=nxt[tmp]){
9 if(a[tmp+1]==y)ans--;
10 if(a[tmp-1]==y)ans--;
11 }
12 for(int tmp=head[x];tmp!=-1;tmp=nxt[tmp])a[tmp]=y;
13 nxt[lsh[x]]=head[y];
14 head[y]=head[x];
15 sum[y]+=sum[x];
16 head[x]=-1;
17 lsh[x]=sum[x]=0;
18 }
19 int main(){
20 memset(sum,0,sizeof(sum));
21 memset(pre,0,sizeof(pre));
22 memset(head,-1,sizeof(head));
23 scanf("%d%d",&n,&m);
24 for(int i=1;i<=n;i++){
25 scanf("%d",&a[i]);
26 pre[a[i]]=a[i];
27 if(a[i]!=a[i-1])ans++;
28 if(head[a[i]]==-1)lsh[a[i]]=i;
29 sum[a[i]]++;
30 nxt[i]=head[a[i]];
31 head[a[i]]=i;
32 }
33 for(int i=1;i<=m;i++){
34 scanf("%d",&s);
35 if(s==2)printf("%d\n",ans);
36 else{
37 scanf("%d%d",&x,&y);
38 if(x==y)continue;
39 if(sum[pre[x]]>sum[pre[y]])swap(pre[x],pre[y]);
40 x=pre[x],y=pre[y];
41 if(!sum[x])continue;
42 work(x,y);
43 }
44 }
45 return 0;
46 }
【BZOJ3123】【SDOI2013】森林
主席树+启发式合并
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #define N 1000000000
7 using namespace std;
8 typedef long long ll;
9 struct edge{
10 int v,next;
11 }a[2000001];
12 int DYZ_HAS_CHANCE,n,m,t,u,v,w,ans=0,tot=0,cnt=0,rt[1000001],ls[5000001],rs[5000001],siz[5000001],sum[1000001],f[1000001],num[1000001],fa[1000001][20],dep[1000001],head[1000001];
13 bool used[1000001];
14 char s[10];
15 int ff(int u){
16 return f[u]==u?u:f[u]=ff(f[u]);
17 }
18 void add(int u,int v){
19 a[++tot].v=v;
20 a[tot].next=head[u];
21 head[u]=tot;
22 }
23 int lca(int u,int v){
24 if(dep[u]<dep[v])swap(u,v);
25 int l=dep[u]-dep[v];
26 for(int i=19;i>=0;i--){
27 if((1<<i)&l)u=fa[u][i];
28 }
29 if(u==v)return u;
30 for(int i=19;i>=0;i--){
31 if(fa[u][i]!=fa[v][i]){
32 u=fa[u][i],v=fa[v][i];
33 }
34 }
35 return fa[u][0];
36 }
37 void updata(int k,int &now,int l,int r,int v){
38 if(!now)now=++cnt;
39 siz[now]=siz[k]+1;
40 if(l==r)return;
41 int mid=(l+r)/2;
42 if(v<=mid)rs[now]=rs[k],updata(ls[k],ls[now],l,mid,v);
43 else ls[now]=ls[k],updata(rs[k],rs[now],mid+1,r,v);
44 }
45 int query(int a1,int a2,int a3,int a4,int l,int r,int v){
46 if(l==r)return l;
47 int mid=(l+r)/2,ret=siz[ls[a1]]+siz[ls[a2]]-siz[ls[a3]]-siz[ls[a4]];
48 if(v<=ret)return query(ls[a1],ls[a2],ls[a3],ls[a4],l,mid,v);
49 else return query(rs[a1],rs[a2],rs[a3],rs[a4],mid+1,r,v-ret);
50 }
51 void dfs(int u,int f){
52 used[u]=true;
53 dep[u]=dep[f]+1;
54 fa[u][0]=f;
55 for(int i=1;i<=19;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
56 updata(rt[f],rt[u],0,N,num[u]);
57 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
58 int v=a[tmp].v;
59 if(v!=f)dfs(v,u);
60 }
61 }
62 void merge(int u,int v){
63 int u1=ff(u),v1=ff(v);
64 f[u1]=v1;
65 sum[v1]+=sum[u1];
66 }
67 int main(){
68 memset(head,-1,sizeof(head));
69 memset(used,0,sizeof(used));
70 memset(rt,0,sizeof(rt));
71 scanf("%d",&DYZ_HAS_CHANCE);
72 scanf("%d%d%d",&n,&m,&t);
73 for(int i=1;i<=n;i++){
74 scanf("%d",&num[i]);
75 f[i]=i;
76 sum[i]=1;
77 }
78 for(int i=1;i<=m;i++){
79 scanf("%d%d",&u,&v);
80 add(u,v);
81 add(v,u);
82 merge(u,v);
83 }
84 for(int i=1;i<=n;i++){
85 if(!used[i])dfs(i,0);
86 }
87 for(int i=1;i<=t;i++){
88 scanf("%s",s);
89 if(s[0]=='Q'){
90 scanf("%d%d%d",&u,&v,&w);
91 u^=ans;
92 v^=ans;
93 w^=ans;
94 int now=lca(u,v);
95 printf("%d\n",ans=query(rt[u],rt[v],rt[now],rt[fa[now][0]],0,N,w));
96 }else{
97 scanf("%d%d",&u,&v);
98 u^=ans;
99 v^=ans;
100 int u1=ff(u),v1=ff(v);
101 if(sum[u1]>sum[v1])swap(u,v);
102 add(u,v);
103 add(v,u);
104 merge(u,v);
105 dfs(u,v);
106 }
107 }
108 return 0;
109 }
【BZOJ3545】【ONTAK2010】Peaks
离线,按照困难度从小到大加边,用线段树维护每个联通块,每次合并联通块即可
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 using namespace std;
7 struct task{
8 int a,b,c,id,ok;
9 }a[1000001];
10 struct treenode{
11 int v,ls,rs;
12 }t[5000001];
13 int n,m,q,tot=0,num[100001],_num[100001],fa[100001],rts[100001],ans[500001];
14 bool cmp(task a,task b){
15 return a.c==b.c?a.ok<b.ok:a.c<b.c;
16 }
17 int ff(int u){
18 return u==fa[u]?u:fa[u]=ff(fa[u]);
19 }
20 void updata(int &u,int l,int r,int v){
21 if(!u)u=++tot;
22 t[u].v=1;
23 if(l==r)return;
24 int mid=(l+r)/2;
25 if(v<=mid)updata(t[u].ls,l,mid,v);
26 else updata(t[u].rs,mid+1,r,v);
27 }
28 int query(int u,int l,int r,int p){
29 if(l==r)return l;
30 int mid=(l+r)/2;
31 if(p<=t[t[u].ls].v)return query(t[u].ls,l,mid,p);
32 else return query(t[u].rs,mid+1,r,p-t[t[u].ls].v);
33 }
34 int merge(int u,int v){
35 if(!u||!v)return u|v;
36 if(!t[u].ls&&!t[u].rs){
37 t[u].v+=t[v].v;
38 return u;
39 }
40 t[u].ls=merge(t[u].ls,t[v].ls);
41 t[u].rs=merge(t[u].rs,t[v].rs);
42 t[u].v=t[t[u].ls].v+t[t[u].rs].v;
43 return u;
44 }
45 int main(){
46 scanf("%d%d%d",&n,&m,&q);
47 for(int i=1;i<=n;i++){
48 scanf("%d",&num[i]);
49 _num[i]=num[i];
50 fa[i]=i;
51 }
52 sort(_num+1,_num+n+1);
53 for(int i=1;i<=n;i++){
54 num[i]=lower_bound(_num+1,_num+n+1,num[i])-_num;
55 }
56 for(int i=1;i<=m;i++){
57 scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
58 a[i].ok=0;
59 }
60 for(int i=m+1;i<=m+q;i++){
61 scanf("%d%d%d",&a[i].a,&a[i].c,&a[i].b);
62 a[i].ok=1;
63 a[i].id=i-m;
64 }
65 sort(a+1,a+m+q+1,cmp);
66 for(int i=1;i<=n;i++)updata(rts[i],1,n,num[i]);
67 for(int i=1;i<=m+q;i++){
68 if(a[i].ok==0){
69 int u=ff(a[i].a),v=ff(a[i].b);
70 if(u!=v){
71 fa[u]=v;
72 rts[v]=merge(rts[u],rts[v]);
73 }
74 }else{
75 int u=ff(a[i].a);
76 if(t[rts[u]].v<a[i].b)ans[a[i].id]=-1;
77 else ans[a[i].id]=_num[query(rts[u],1,n,t[rts[u]].v-a[i].b+1)];
78 }
79 }
80 for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
81 return 0;
82 }
【BZOJ2212】【POI2011】Tree Rotation
直接从下往上线段树合并即可
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 using namespace std;
7 typedef long long ll;
8 struct treenode{
9 ll v;
10 int ls,rs;
11 }t[6000001];
12 int n,tot=0,cnt=1,num[2000001],ls[2000001],rs[2000001],rts[2000001];
13 ll ans=0,ans1,ans2;
14 void read(int u){
15 scanf("%d",&num[u]);
16 if(!num[u]){
17 read(ls[u]=++cnt);
18 read(rs[u]=++cnt);
19 }
20 }
21 void updata(int &u,int l,int r,int v){
22 if(!u)u=++tot;
23 if(l==r){
24 t[u].v=1;
25 return;
26 }
27 int mid=(l+r)/2;
28 if(v<=mid)updata(t[u].ls,l,mid,v);
29 else updata(t[u].rs,mid+1,r,v);
30 t[u].v=t[t[u].ls].v+t[t[u].rs].v;
31 }
32 int merge(int u,int v){
33 if(!u||!v)return u|v;
34 ans1+=(ll)t[t[u].rs].v*t[t[v].ls].v;
35 ans2+=(ll)t[t[u].ls].v*t[t[v].rs].v;
36 t[u].ls=merge(t[u].ls,t[v].ls);
37 t[u].rs=merge(t[u].rs,t[v].rs);
38 t[u].v=t[t[u].ls].v+t[t[u].rs].v;
39 return u;
40 }
41 void dfs(int u){
42 if(!u)return;
43 dfs(ls[u]);
44 dfs(rs[u]);
45 if(!num[u]){
46 ans1=ans2=0;
47 rts[u]=merge(rts[ls[u]],rts[rs[u]]);
48 ans+=min(ans1,ans2);
49 }
50 }
51 int main(){
52 scanf("%d",&n);
53 read(1);
54 for(int i=1;i<=cnt;i++){
55 if(num[i])updata(rts[i],1,n,num[i]);
56 }
57 dfs(1);
58 printf("%lld",ans);
59 return 0;
60 }
现在搞专题沉迷摸鱼,一天平均只有两道题,颓废力max