我還是太NAIVE了
一道並查集的題又對拍又眼調還花了3hQAQ
史
題目大意
關於並查集的合併與查詢祖先,要維護時間戳,強制在線
要維護一個固定的值,顯然我們不能路徑壓縮,至於合併有兩種方法(複雜度都是nlogn)
一種是啓發式合併,每次按size從小的往大的合併
另一種是按秩合併,就是dep從底往上遞增
因爲沒有路徑壓縮,所以要存Fa()的返回值
核心程序
啓發式合併
void Merge(int p,int q){
num++;
int u=Fa(p,INF),v=Fa(q,INF);
if(u==v)return ;
if(sz[u]<sz[v])swap(u,v);
f[v]=num;
sz[u]+=sz[v];
fa[v]=u;
}
按秩合併
void Merge(int p,int q){
num++;
int u=Fa(p,INF),v=Fa(q,INF);
if(u==v)return ;
if(d[u]<d[v])swap(u,v);
f[v]=num;
d[u]=max(d[u],d[v]+1);
fa[v]=u;
}
FindFather
lim是題目的限制時間戳
如果要判斷是否在同一個並查集就比較Fa(p,INF)和Fa(q,INF)是否相等
int Fa(int x,int lim){
return (fa[x]!=x)&&(f[x]<=lim)?Fa(fa[x],lim):x;
}
樹
題目大意
插點到一棵空二叉查找樹中,查詢的節點深度和
分析
可以在線用set/map維護,l和r表示now的左右迭代器,時間O(nlogn)
dep【*now】=max(dep【*l】,dep【*r】)+1
或者每次找到最先插入的點,遞歸找左右子樹
這個用rmq/線段樹維護插入順序區間最小值就可以做到O(nlogn)
附上map做法的程序
#include<cstdio>
#include<map>
#include<algorithm>
#define se second
#define fr first
#define mp(p,q) make_pair(p,q)
using namespace std;
typedef long long LL;
LL ans;
map<int,int>M;
int n,p;
map<int,int>::iterator l,r;
int main(){
scanf("%d",&n);
M[0]=-1;
M[n+1]=-1;
for(int i=1;i<=n;i++){
scanf("%d",&p);
M[p]=0;
l=r=M.lower_bound(p);
M[p]=max((*(--l)).se,(*(++r)).se)+1;
// printf("%d %d %d %d\n",(*l).fr,(*r).fr,(*l).se,(*r).se);
ans+=M[p];
printf("%lld\n",ans);
}
}