启发式合并
这两天做了两道“启发式合并”的题,似乎“启发式合并”就是每次把节点数小的依次放到节点数大的地方,对只有合并操作的题能做到logn的时间复杂度
[BZOJ1483][HNOI2009]梦幻布丁:链表的启发式合并
每次把短的安到长的上面再判断
#include<cstdio> #define N 1111111 using namespace std; int n,m,i,ii,p,ans,x,y,t,tmp,a[N],now[N],first[N],next[N],color[N],ft[N]; int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&a[i]); color[a[i]]++; ft[a[i]]=a[i]; if(a[i]!=a[i-1])ans++; if(!first[a[i]])first[a[i]]=i; next[now[a[i]]]=i; now[a[i]]=i; } for(ii=1;ii<=m;ii++){ scanf("%d",&p); if(p==2)printf("%d\n",ans);else{ scanf("%d%d",&x,&y); if(x==y)continue; if(color[ft[x]]>color[ft[y]])t=ft[x],ft[x]=ft[y],ft[y]=t; x=ft[x];y=ft[y]; if(color[x]==0)continue; for(i=first[x];i;i=next[i]){ tmp=i; if(a[i+1]==y)ans--; if(a[i-1]==y)ans--; } for(i=first[x];i;i=next[i])a[i]=y; next[tmp]=first[y];first[y]=first[x]; color[y]+=color[x];first[x]=color[x]=0; } } }
[BZOJ2733][HNOI2012]永无乡:Splay的启发式合并
每次把小的Splay暴力拆掉,加入到大的Splay上面,第一次写无根Splay、、
#include<cstdio> #include<iostream> #define N 111111 using namespace std; int n,m,T,x,y,p,q,k,i,top,father[N],son[N][2],size[N],val[N],num[N],zhi[N];char s[9]; int find(int x){while(father[x])x=father[x];return x;} void pushup(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;} void newnode(int x,int fa,int da){father[x]=fa;size[x]=1;val[x]=da;son[x][0]=son[x][1]=0;} void rotate(int x,int k){ int y=father[x]; son[y][!k]=son[x][k];father[son[y][!k]]=y; son[father[y]][son[father[y]][1]==y]=x; father[x]=father[y]; son[x][k]=y;father[y]=x; pushup(y); } void splay(int x){ while(father[x]){ int y=father[x],z=father[y],f=(son[z][0]==y); if(!z)rotate(x,son[y][0]==x);else{ if(son[y][f]==x)rotate(x,!f);else rotate(y,f); rotate(x,f); } } pushup(x); } void insert(int x,int number,int key){ while(son[x][key>val[x]])size[x]++,x=son[x][key>val[x]]; size[x]++;son[x][key>val[x]]=number; newnode(number,x,key); splay(number); } void intour(int x){ if(!x)return;num[++top]=x;zhi[top]=val[x]; intour(son[x][0]);intour(son[x][1]); } int find(int x,int k){ if(size[son[x][0]]+1==k)return x; if(size[son[x][0]]>=k)return find(son[x][0],k); return find(son[x][1],k-size[son[x][0]]-1); } void link(){ scanf("%d%d",&x,&y);p=find(x);q=find(y); if(p!=q){ if(size[p]>size[q])swap(p,q),swap(x,y); top=0;intour(p); for(int i=1;i<=top;i++)insert(q,num[i],zhi[i]); } } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d",&val[i]),size[i]=1; for(i=1;i<=m;i++)link(); scanf("%d",&T); while(T--){ scanf("%s",s); if(s[0]=='B')link();else{ scanf("%d%d",&x,&k); splay(x); if(k>size[x])puts("-1");else printf("%d\n",find(x,k)); } } }
似乎启发式合并都是HNOI的,看来ZJOI是不会考这么简单的题目的、、
2015年6月17日 06:51
时间复杂度证明你会么。。