bzoj 3551: [ONTAK2010]Peaks加强版

Description

【题目描述】同3545

Input

第一行三个数N,M,Q。

第二行N个数,第i个数为h_i

接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径。

接下来Q行,每行三个数v x k,表示一组询问。v=v xor lastans,x=x xor lastans,k=k xor lastans。如果lastans=-1则不变。

Output

同3545

Sample Input

Sample Output

HINT

【数据范围】同3545

Source

Kruskal重构树,就是在合并的时候新建一个节点,点权为边权。。

有一些性质:

1.这是一棵二叉树。(没卵用)

2.原树点都是叶子结点。

3.子结点比父结点点权小(大根堆)。

4.原树与新树两点间路径最大值与新树相等,且等于新树两点lcalca点权。

所以只要从v点往上跳到深度最浅的<=x的点,然后查询子树第k大即可。。

然而死活RE,弃了弃了。。。

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define RG register
using namespace std;
typedef long long ll;
const int N=400050;
int n,m,Q,sz,fa[N],rt[N],ls[N*20],rs[N*20],sum[N*20],hsh[N],Fa[N];
int id[N],dfn[N],ed[N],h[N],v[N],size[N],son[N],top[N];
struct data{
  int a,b,c;
}e[500050];
bool cmp(const data &a,const data &b){return a.c<b.c;}
int head[N],to[N],nxt[N],cnt,tt,tot,res;
inline void lnk(int x,int y){
  to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
}
inline int find(int x){
  if(x!=fa[x]) fa[x]=find(fa[x]);
  return fa[x];
}
inline void dfs1(int x,int f){
  size[x]=1;
  for(RG int i=head[x];i;i=nxt[i]){
    int y=to[i];
    if(y!=f){
      Fa[y]=x;dfs1(y,x);size[x]+=size[y];
      if(size[y]>size[son[x]]) son[x]=y;
    }
  }
}
inline void dfs2(int x,int ff){
  dfn[x]=++tot;id[tot]=x;top[x]=ff;
  if(son[x]) dfs2(son[x],ff);
  for(RG int i=head[x];i;i=nxt[i]){
    int y=to[i];
    if(y!=Fa[x]&&y!=son[x]) dfs2(y,y);
  }
  ed[x]=tot;
}
inline void insert(RG int x,RG int &y,RG int l,RG int r,RG int u){
  y=++sz;ls[y]=ls[x],rs[y]=rs[x];sum[y]=sum[x]+1;
  if(l==r) return;
  RG int mid=(l+r)>>1;
  if(u<=mid) insert(ls[x],ls[y],l,mid,u);
  else insert(rs[x],rs[y],mid+1,r,u);
}
inline int query(RG int x,RG int y,RG int l,RG int r,RG int k){
  if(l==r) return l;
  RG int mid=(l+r)>>1;
  if(sum[ls[y]]-sum[ls[x]]>=k) return query(ls[x],ls[y],l,mid,k);
  else return query(rs[x],rs[y],mid+1,r,k-(sum[ls[y]]-sum[ls[x]]));
}
inline int jump(RG int x,RG int lim){
  RG int j=x;
  while(x&&v[top[x]]<=lim){
    j=top[x],x=Fa[top[x]];
  }
  if(v[x]>lim||v[top[x]]<=lim) return j;
  RG int l=dfn[top[x]],r=dfn[x],ret=0;
  while(l<=r){
    RG int mid=(l+r)>>1;
    if(v[id[mid]]<=lim) r=mid-1,ret=mid;
    else l=mid+1;
  }
  return id[ret];
}
int main(){
  freopen("Peaks.in","r",stdin);
  freopen("Peaks.out","w",stdout);
  scanf("%d%d%d",&n,&m,&Q);
  for(RG int i=1;i<=n;i++) scanf("%d",&h[i]),hsh[++res]=h[i];
  for(RG int i=1;i<=m;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
  sort(e+1,e+1+m,cmp);for(int i=1;i<=n;i++) fa[i]=i;tt=n;
  for(RG int i=1;i<=m;i++){
    RG int x=find(e[i].a),y=find(e[i].b);
    if(y!=x){
      tt++;v[tt]=e[i].c;fa[x]=fa[y]=tt;fa[tt]=tt;
      lnk(tt,x);lnk(tt,y);
    }
  }
  for(RG int i=1;i<=tt;i++){
    RG int g=find(i);
    if(!dfn[g]) dfs1(g,g),dfs2(g,g);
  }
  sort(hsh+1,hsh+res+1);res=unique(hsh+1,hsh+1+res)-hsh-1;
  for(RG int i=1;i<=tot;i++){
    rt[i]=rt[i-1];
    if(id[i]<=n) insert(rt[i],rt[i],1,res,lower_bound(hsh+1,hsh+1+res,h[id[i]])-hsh);
  }
  RG int lastans=0;
  while(Q--){
    RG int u,x,k;scanf("%d%d%d",&u,&x,&k);
    u^=lastans,x^=lastans,k^=lastans;
    RG int Lca=jump(u,x);
    if(sum[rt[ed[Lca]]]-sum[rt[dfn[Lca]-1]]<k) puts("-1"),lastans=0;
    else lastans=query(rt[dfn[Lca]-1],rt[ed[Lca]],1,res,(sum[rt[ed[Lca]]]-sum[rt[dfn[Lca]-1]])-k+1),printf("%d\n",hsh[lastans]);
  }
  return 0;
}

  

时间: 2024-10-14 04:39:31

bzoj 3551: [ONTAK2010]Peaks加强版的相关文章

bzoj 3551 [ONTAK2010]Peaks加强版(kruskal,主席树,dfs序)

Description [题目描述]同3545 Input 第一行三个数N,M,Q. 第二行N个数,第i个数为h_i 接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径. 接下来Q行,每行三个数v x k,表示一组询问.v=v xor lastans,x=x xor lastans,k=k xor lastans.如果lastans=-1则不变. Output 同3545 [思路] Kruskal+主席树+dfs序 一个叫kruskal重构树的方法QWQ.在kruskal合

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线 3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953 强制在线没法排序 启发式合并也就用不了了 Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题 这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上 比如说样例的树就建成了这样 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二

bzoj 3545&amp;&amp;3551: [ONTAK2010]Peaks &amp;&amp;加强版 平衡树&amp;&amp;并查集合并树&amp;&amp;主席树

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MB Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. Input 第一行三个数N,M,Q.第二行N个数,第i个数为h_i接下来M行,每行

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

bzoj3551 [ONTAK2010]Peaks加强版

Description //[题目描述]同3545 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. Input 第一行三个数N,M,Q. 第二行N个数,第i个数为h_i 接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径. 接下来Q行,每行三个数v x k,表

ONTAK2010 Peaks加强版

给一个图,每个点有点权,$q$ 次询问从 $x$ 开始只走点权小于等于 $y$ 的路径能到的点中第 $k$ 大的点权,无解输出 -1 强制在线 请注意因为这个 sb 博主为了描述方便,这里的题目描述用的字母跟原题有出入,题解跟跟这里的题目描述一样,不一定跟 bzoj 上一样( $n \leq 10^5$ $m,q \leq 5 \times 10^5$ 点权$\leq 10^9$ sol: Kruskal 重构树,每次能走的是一个子树 于是我们可以先倍增找到能走的最高的点(倍增找到重构树上点权小

bzoj3545 [ONTAK2010]Peaks、bzoj3551 [ONTAK2010]Peaks加强版

题目描述: bzoj3545,luogu bzoj3551 题解: 重构树+线段树合并. 可以算是板子了吧. 代码(强制在线): #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 100050; const int M = 5*N; template<typename T> inline void read(T&x) {

dtoi1927 [ONTAK2010]Peaks加强版

题意: 给一个n个点的图,每个点有权值,每条边有权值.q次询问,每次询问从a出发不经过边权大于x的边能够到达的所有点中,点权第k大的值. n<=100000,q<=500000 题解:      点权第k大的值,容易想到可持久化线段树,问题就在于如何把要查询的点转化为一段连续的区间. 考虑建立Kruskal重构树.这种树有一个性质,经过两个点的路径的边权最大值就是在重构树上的lca的点权. 因此可以倍增a的父亲到达最后一个点权小于等于x的节点,然后所有能到达的点就是其子树. 那么就在dfs序上