Peaks

link

重构树的模板。另一道也算是板子的题目归程还没做,以后再填坑。今天过得十分凄惨,上午调一道树上主席树怎么也调不过,下午调一个重构树的模板也调不过,凄凄惨惨戚戚啊啊啊,而且错误还很玄学……

说回题目本身。重构树是建立在最小生成树Kruskal算法的基础之上的,可以处理类似于“只能走边权大于/小于某个特定值的边”且询问次数较多的题目。算法考虑维护一个二叉树森林,每次合并两个点集时建立一个节点,点权为该边权且该点的两个儿子是两个点集对应的二叉树根节点。由于Kruskal是按边权从小到大插入,可以想到最后得到的二叉树权值一定满足上小下大(某些题目可能不一样),也就是说它最后会是一个不一定平衡的堆,这说明了假如限制了边权上界,某个叶子x可以到达它的祖先y,那么y子树内所有点都可以由x到达。又由于它是最小生成树的衍生物,可以证明对于两个叶子节点,在图上不存在一条连接两个点的路径使得该路径上最小边权小于二叉树上两点的路径上最小边权,这说明了假如叶子x不能到y,那么y以上的所有节点都无法到达。显然这满足单调性,可以用倍增来快速求解。这使得重构树的复杂度为\(O(QlogNw)\)级别的。

至于这道题,由于要询问区间k大,考虑用主席树来维护,可以做到\(O(NlogN^2)\)。鬼知道为什么离散化出了锅,改了就好了,害的我调了一下午……

#include<cstdio>
#include<cstring>
#include<algorithm>
//#define zczc
using namespace std;
const int N=200010;
const int M=500010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int min(int s1,int s2){return s1<s2?s1:s2;}
inline int max(int s1,int s2){return s1<s2?s2:s1;}

struct ee{int a,b,val;}se[M];
inline bool cmp(ee s1,ee s2){return s1.val<s2.val;}

int m,n,q,s[N];

int cnt,f[N];
inline int find(int wh){return f[wh]==wh?wh:f[wh]=find(f[wh]);}

struct edge{int t,next;}e[N<<1];
int esum,head[N],a[N],b[N];
inline void add(int fr,int to){
	e[++esum]=(edge){to,head[fr]};head[fr]=esum;
}

struct node{int size,l,r,lc,rc;}t[N<<5];int kk,root[N];
int build(int l,int r){
	int wh=++kk,mid=l+r>>1;t[wh].l=l,t[wh].r=r;
	if(l==r)return wh;
	return t[wh].lc=build(l,mid),t[wh].rc=build(mid+1,r),wh;
}
int insert(int x,int pl){
	int wh=++kk,mid=t[x].l+t[x].r>>1;t[wh]=t[x];t[wh].size++;
	if(t[x].l==t[x].r)return wh;
	if(pl<=mid)t[wh].lc=insert(t[x].lc,pl);
	else t[wh].rc=insert(t[x].rc,pl);return wh;
}
void work(int x,int y,int k){
	if(t[x].l==t[x].r){printf("%d\n",s[t[x].l]);return;}
	int mid=t[x].l+t[x].r>>1,sum=t[t[y].rc].size-t[t[x].rc].size;
	if(sum>=k)work(t[x].rc,t[y].rc,k);
	else work(t[x].lc,t[y].lc,k-sum);
}

int nxt[N][28],l[N],r[N],tot;
void dfs(int wh,int fa){
	nxt[wh][0]=fa;for(int i=1;i<=25;i++)nxt[wh][i]=nxt[nxt[wh][i-1]][i-1];
	l[wh]=tot;
	for(int i=head[wh];i;i=e[i].next)dfs(e[i].t,wh);
	if(!head[wh]){
		l[wh]=++tot;
		root[tot]=insert(root[tot-1],a[wh]);
	}
	r[wh]=tot;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);read(q);memset(l,0x3f,sizeof(l));
	for(int i=1;i<=2*m;i++)f[i]=i;cnt=m;
	for(int i=1;i<=m;i++){read(a[i]);s[i]=a[i];}
	sort(s+1,s+m+1);int num=unique(s+1,s+m+1)-s-1;
	for(int i=1;i<=m;i++)a[i]=lower_bound(s+1,s+m+1,a[i])-s;
	for(int i=1;i<=n;i++){read(se[i].a);read(se[i].b);read(se[i].val);}
	sort(se+1,se+n+1,cmp);
	for(int i=1;i<=n;i++){
		int p1=se[i].a,p2=se[i].b,v=se[i].val;
		int f1=find(p1),f2=find(p2);
		if(f1==f2)continue;
		f[f1]=f[f2]=++cnt;add(cnt,f1);add(cnt,f2);b[cnt]=v;
	}
	
	root[0]=build(1,m);dfs(cnt,0);
	int s1,s2,s3;
	while(q--){
		read(s1);read(s2);read(s3);
		for(int i=25;i>=0;i--)if(nxt[s1][i]&&b[nxt[s1][i]]<=s2)s1=nxt[s1][i];
		if(t[root[r[s1]]].size-t[root[l[s1]]].size<s3)printf("-1\n");
		else work(root[l[s1]],root[r[s1]],s3);
	}
	
	return 0;
}
posted @ 2022-03-12 18:19  Feyn618  阅读(139)  评论(0)    收藏  举报