我还是太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);
}
}