今天闲来无事,无题可做(因为太菜了开的题都不会做)
于是打开了2017IOI国家候选队的论文,徐明宽的<<非常规大小分块算法初探>>
http://www.noi.cn/noi-news/noi/786-ctsc2017
然后略过了前面一些看不太懂的东西.
看到了method of four russians这个lca算法
预处理O(n),查询O(1),还是在线的
看起来很牛逼,于是就仔细理解了一下
又上网找了找资料,结果发现网上没什么资料
于是就根据论文自己打了一打.
然后就不好了,打了一个下午.
原文对做法讲的已经十分清楚了
首先dfs,处理出dfs序(一个点出现多次的那种)
然后转化为正负1RMQ问题(因为相邻两点深度差最多为1)
然后正负1RMQ问题可以分块解决
块之间用普通RMQ倍增法维护
一个块内直接用一种难以描述的方法维护
具体来说是这样
设一个块内有s个元素,分别为a[0]-a[s-1]
先差分,b[i]=a[i+1]-a[i]
i属于[0,s-2]
然后所有的块就可被归为2^(s-1)类,因为b[i]不是-1,就是1(0?不存在的,一条边两边点的深度不可能相等)
每一类处理出在一定范围内最小值的位置
然后就好了
查询时乱搞一波
具体来说就是包含块算块,注意边界,无块就用维护块内信息的数组回答.
(话说这方法考试时真能打吗?直接被续光时间)
至于这个算法的常数,详见论文.
例题
上代码
//method of four russians //prework O(n) ask O(1) #include<bits/stdc++.h> using namespace std; const int N=500010,M=500010,KK=250010,P=19,S_2=256,S=9; int b[N<<1],ne[N<<1],fi[N],k,n,m,root,s; inline void read(int &x){ x=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); } void add(int x,int y){ b[++k]=y; ne[k]=fi[x]; fi[x]=k; b[++k]=x; ne[k]=fi[y]; fi[y]=k; } void in(){ read(n); read(m); read(root); for (int i=1; i<n; ++i){ int x,y; read(x); read(y); add(x,y); } s=log2(n)/2; if (!s) s=1; // cerr<<"in"<<s<<endl; } int a[(N<<1)+10],deep[N],la[N],len; void dfs(int x){ la[x]=len; a[len++]=x; for (int j=fi[x]; j; j=ne[j]) if (!deep[b[j]]){ deep[b[j]]=deep[x]+1; dfs(b[j]); la[x]=len; a[len++]=x; } } int f[P][KK],lg[KK],minn[S_2][S][S],dep[S],re[KK]; void prework(){ deep[0]=INT_MAX; deep[root]=1; dfs(root); // for (int i=0; i<len; i++) cerr<<a[i]<<" "; puts(""); // for (int i=0; i<len; i++) cerr<<deep[a[i]]<<" "; puts(""); /*-----------------------------------------------------*/ //S numbers,s-1 interval,less than 2^(s-1) for (int i=0; i<(1<<(s-1)); i++) for (int j=0; j<s; j++){//from 0 minn[i][j][j]=j; dep[j]=0; for (int k=j+1; k<s; k++){ if ((i>>(k-1))&1) dep[k]=dep[k-1]+1; else dep[k]=dep[k-1]-1; if (dep[k]<=dep[minn[i][j][k-1]]) minn[i][j][k]=k; else minn[i][j][k]=minn[i][j][k-1]; // cerr<<i<<" "<<j<<" "<<k<<" "<<dep[k]<<" "<<minn[i][j][k]<<endl; system("pause"); } } for (int i=0; i<=(len-1)/s; i++){ int l=i*s,t=0; for (int j=0; j<s-1; j++) if (deep[a[l+1+j]]-deep[a[l+j]]!=-1) t|=1<<j; re[i]=t; // cerr<<i<<" "<<re[i]<<endl; } /*----------------------------------------------------*/ int kk=(len-1)/s,p=(int)log2(kk+1)+1; lg[1]=0; for (int i=2; i<=kk; i++) lg[i]=lg[i>>1]+1; /*----------------------------------------------------*/ for (int i=0; i<len; i++) if (deep[f[0][i/s]]>deep[a[i]]) f[0][i/s]=a[i]; for (int j=1; j<=p; j++) for (int i=0; i<=kk; i++) if (i+(1<<(j-1))<=kk){ int u=f[j-1][i],v=f[j-1][i+(1<<(j-1))]; if (deep[u]<deep[v]) f[j][i]=u; else f[j][i]=v; }else break; /*-----------------------------------------------------*/ } int ask(int l,int r){ if (l>r) l^=r^=l^=r; int sl=l/s,sr=r/s,ll=l-sl*s,rr=r-sr*s; if (sl!=sr){ int res=0,u,v; if (sl+1<=sr-1){ int j=lg[sr-sl-1]; u=f[j][sl+1]; v=f[j][sr-(1<<j)];//from sl+1 to sr-1 if (deep[u]<deep[v]) res=u; else res=v; } u=a[r-rr+minn[re[sr]][0][rr]]; if (deep[u]<deep[res]) res=u; v=a[l-ll+minn[re[sl]][ll][s-1]]; if (deep[v]<deep[res]) res=v; return res; } else return a[l-ll+minn[re[sl]][ll][rr]]; } void ans_the(){ while (m--){ int x,y; scanf("%d%d",&x,&y); printf("%d ",ask(la[x],la[y])); } } int main(){ in(); prework(); ans_the(); }