欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

P4899-[IOI2018]werewolf 狼人【Kruskal重构树,主席树】

发布时间:2023/12/3 编程问答 37 豆豆
生活随笔 收集整理的这篇文章主要介绍了 P4899-[IOI2018]werewolf 狼人【Kruskal重构树,主席树】 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

正题

题目链接:https://www.luogu.com.cn/problem/P4899


题目大意

nnn个点的一张无向图,每次询问(s,t,l,r)(s,t,l,r)(s,t,l,r)表示询问能否找到一条s∼ts\sim tst的路径使得该路径可以分割成点的序号在[l,n][l,n][l,n][1,r][1,r][1,r]的两段。


解题思路

首先对原图构造两棵KruskalKruskalKruskal生成树,分别是最小序号最大的和最大序号最小的路径,然后我们每次询问是可以分别在树上跳到一个最高的位置。

然后问题就转换为询问两个树的子树交集,考虑使用主席树,对于第dfn1xdfn1_xdfn1x棵主席树上的第dfn2xdfn2_xdfn2x个位置加权值(dfn1xdfn1_xdfn1xdfn2xdfn2_xdfn2x分别表示xxx在第1/21/21/2棵树上的dfsdfsdfs序)。然后主席树询问即可。

时间复杂度O(nlog⁡n)O(n\log n)O(nlogn)


codecodecode

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=6e5,T=20; struct edge{int x,y,w; }e[N]; struct node{int to,next; }; struct Kul{node a[N<<1];int tot,cnt,num,fa[N],ls[N],w[N],dfn[N],rfn[N],ed[N],f[N][T+1];void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;//printf("%d %d\n",x,y);return;}int find(int x){return (fa[x]==x)?x:(fa[x]=find(fa[x]));}void addp(int x,int y,int val){if(find(x)==find(y))return;w[++num]=val; addl(num,find(x));addl(num,find(y));fa[find(y)]=num;fa[find(x)]=num;return;}void dfs(int x){dfn[++cnt]=x;rfn[x]=cnt;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;f[y][0]=x;dfs(y);}ed[x]=cnt;}void init(){for(int i=num;i>=1;i--)if(!f[i][0])dfs(i);w[0]=num+1;for(int j=1;j<=T;j++)for(int i=1;i<=num;i++)f[i][j]=f[f[i][j-1]][j-1];return;}int jump(int x,int val){for(int i=T;i>=0;i--)if(w[f[x][i]]<=val)x=f[x][i];return x;} }k1,k2; int n,m,q,cnt,rt[N],x[N],y[N],w[N<<5],ls[N<<5],rs[N<<5]; int Insert(int y,int l,int r,int pos){int x=++cnt;w[x]=w[y]+1;if(l==r)return x;int mid=(l+r)>>1;if(pos<=mid)ls[x]=Insert(ls[y],l,mid,pos),rs[x]=rs[y];else rs[x]=Insert(rs[y],mid+1,r,pos),ls[x]=ls[y];return x; } int Ask(int x,int y,int L,int R,int l,int r){if(!(w[y]-w[x]))return 0;if(l==L&&R==r)return w[y]-w[x];int mid=(L+R)>>1;if(r<=mid)return Ask(ls[x],ls[y],L,mid,l,r);if(l>mid)return Ask(rs[x],rs[y],mid+1,R,l,r);return Ask(ls[x],ls[y],L,mid,l,mid)+Ask(rs[x],rs[y],mid+1,R,mid+1,r); } bool cmp(edge x,edge y) {return x.w<y.w;} int main() {scanf("%d%d%d",&n,&m,&q);k1.num=k2.num=n;for(int i=1;i<=n+m;i++)k1.fa[i]=k2.fa[i]=i;for(int i=1;i<=m;i++)scanf("%d%d",&e[i].x,&e[i].y),e[i].x++,e[i].y++,e[i].w=max(e[i].x,e[i].y);sort(e+1,e+1+m,cmp);for(int i=1;i<=m;i++)k1.addp(e[i].x,e[i].y,e[i].w),e[i].w=min(e[i].x,e[i].y);sort(e+1,e+1+m,cmp);for(int i=m;i>=1;i--)k2.addp(e[i].x,e[i].y,n-e[i].w+1);k1.init();k2.init();for(int i=1;i<=k1.num;i++)if(k1.dfn[i]<=n)rt[i]=Insert(rt[i-1],1,k2.num,k2.rfn[k1.dfn[i]]);else rt[i]=rt[i-1];while(q--){int l,r,s,t;scanf("%d%d%d%d",&s,&t,&l,&r);s++;t++;l++;r++;r=k1.jump(t,r);l=k2.jump(s,n-l+1);//printf("%d %d\n",l,r);if(Ask(rt[k1.rfn[r]-1],rt[k1.ed[r]],1,k2.num,k2.rfn[l],k2.ed[l])) printf("1\n");else printf("0\n");}return 0; }

总结

以上是生活随笔为你收集整理的P4899-[IOI2018]werewolf 狼人【Kruskal重构树,主席树】的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。