标签:algo -- begin 连续 lib 线段树 就是 问题 pac
题意:
给一个n个点的图,每个点有权值,每条边有权值。q次询问,每次询问从a出发不经过边权大于x的边能够到达的所有点中,点权第k大的值。
n<=100000,q<=500000
题解:
点权第k大的值,容易想到可持久化线段树,问题就在于如何把要查询的点转化为一段连续的区间。
考虑建立Kruskal重构树。这种树有一个性质,经过两个点的路径的边权最大值就是在重构树上的lca的点权。
因此可以倍增a的父亲到达最后一个点权小于等于x的节点,然后所有能到达的点就是其子树。
那么就在dfs序上建立可持久化线段树,每次查询第k大即可。
#include<cstdio> #include<vector> #include<algorithm> #include<cstdlib> using namespace std; const int INF=1e9; int n,m,q,fa[22][200002],f[200002],rt[200002],ct,cnt,dfn[200002],dfq[200002],df,zjds[200002],ans; vector<int>y[200002]; typedef struct{ int u,v,w; }Q; bool cmp(Q aa,Q bb){ return (aa.w<bb.w); } typedef struct{ bool u;int a; }P; typedef struct{ int sum,ls,rs; }PP; Q g[500002]; P t[200002]; PP p[8000002]; int find(int x){ if (f[x]==x)return x; return f[x]=find(f[x]); } void dfs(int x,int faa){ fa[0][x]=faa;dfn[x]=++df;dfq[df]=x;zjds[x]=1; for (int i=0;i<y[x].size();i++) { dfs(y[x][i],x);zjds[x]+=zjds[y[x][i]]; } } void gengxin(int r1,int r2,int begin,int end,int wz){ if (begin==end) { p[r2].sum=p[r1].sum+1; return; } int mid=(begin+end)/2; if (wz<=mid) { p[r2].ls=++cnt;p[r2].rs=p[r1].rs; gengxin(p[r1].ls,p[r2].ls,begin,mid,wz); } else { p[r2].rs=++cnt;p[r2].ls=p[r1].ls; gengxin(p[r1].rs,p[r2].rs,mid+1,end,wz); } p[r2].sum=p[p[r2].ls].sum+p[p[r2].rs].sum; } int chaxun(int r1,int r2,int begin,int end,int k){ if (k>p[r2].sum-p[r1].sum)return -1; if (begin==end)return begin; int mid=(begin+end)/2; if (p[p[r2].rs].sum-p[p[r1].rs].sum>=k)return chaxun(p[r1].rs,p[r2].rs,mid+1,end,k); else return chaxun(p[r1].ls,p[r2].ls,begin,mid,k-(p[p[r2].rs].sum-p[p[r1].rs].sum)); } int main() { scanf("%d%d%d",&n,&m,&q); for (int i=1;i<=n;i++) { t[i].u=1;scanf("%d",&t[i].a); } for (int i=1;i<=m;i++)scanf("%d%d%d",&g[i].u,&g[i].v,&g[i].w); for (int i=1;i<=2*n;i++)f[i]=i; sort(g+1,g+m+1,cmp);ct=n; for (int i=1;i<=m;i++) if (find(g[i].u)!=find(g[i].v)) { ct++;t[ct].u=0;t[ct].a=g[i].w; y[ct].push_back(find(g[i].u));y[ct].push_back(find(g[i].v)); f[find(g[i].u)]=ct;f[find(g[i].v)]=ct; } for (int i=ct;i>=1;i--) if (!dfn[i])dfs(i,0); for (int i=1;i<=20;i++) for (int j=1;j<=ct;j++) fa[i][j]=fa[i-1][fa[i-1][j]]; for (int i=1;i<=df;i++) { if (!t[dfq[i]].u) { rt[i]=rt[i-1];continue; } rt[i]=++cnt; gengxin(rt[i-1],rt[i],0,INF,t[dfq[i]].a); } for (int i=1;i<=q;i++) { int v,x,k; scanf("%d%d%d",&x,&v,&k); if (ans!=-1){v^=ans;x^=ans;k^=ans;} for (int i=20;i>=0;i--) if (fa[i][x] && t[fa[i][x]].a<=v)x=fa[i][x]; printf("%d\n",ans=chaxun(rt[dfn[x]-1],rt[dfn[x]+zjds[x]-1],0,INF,k)); } return 0; }
标签:algo -- begin 连续 lib 线段树 就是 问题 pac
原文地址:https://www.cnblogs.com/1124828077ccj/p/12239298.html