【bzoj3551】【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
题解:既然强制在线。那么我们就无法离线然后平衡树+启发式合并了。
            考虑做最小生成树。
            合并两个点的时候我们不直接合并。而是新建一个点。然后把两个点连向他,并设为两个点的父亲。
            这个点的权值我们设成连接这两个点的边的边权。
            这样可以新建出来一棵树。
            这棵树首先肯定是个大根堆。
            这棵树上的两点间的lca的权值等于原图中两点间路径上的边的最大值.
            然后对于每次询问我们可以倍增出最浅的权值不超过给定值的那个点.那个点所控制的子树的所有叶子节点就是能到达的所有点。
            主席树+dfs序查询即可。
代码:
           
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
int cnt,point[N],next[N],s[N],n,m,p,h[N],f[N],t,v[N],vis[N],top,fa[N][20],mx[N][20],bg[N],ed[N];
int deep[N],q[N*3],root[N*3],ls[N*25],rs[N*25],sum[N*25],sz,ans(-1);
struct use{int st,en,v;}e[N],b[N*3];
bool cmp(use a,use b){return a.v<b.v;}
void add(int x,int y){next[++cnt]=point[x];point[x]=cnt;e[cnt].en=y;}
int fd(int x){return x==f[x]?x:f[x]=fd(f[x]);}
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void dfs(int x){
  q[++top]=x;vis[x]=1;
  for (int i=1;(1<<i)<=deep[x];i++) 
    fa[x][i]=fa[fa[x][i-1]][i-1],mx[x][i]=max(mx[x][i-1],mx[fa[x][i-1]][i-1]);
  for (int i=point[x];i;i=next[i]){
       fa[e[i].en][0]=x;mx[e[i].en][0]=v[x];
       deep[e[i].en]=deep[x]+1;dfs(e[i].en);
    }   
  if (x>n) q[++top]=x;
}
void insert(int l,int r,int &x,int y,int v){
    x=++sz;sum[x]=sum[y]+1;
    if(l==r)return;
    ls[x]=ls[y];rs[x]=rs[y];
    int mid=(l+r)>>1;
    if(v<=mid)insert(l,mid,ls[x],ls[y],v);else insert(mid+1,r,rs[x],rs[y],v);
}
void pre(){
  t=n;sort(b+1,b+m+1,cmp);  
  for (int i=1;i<=m;i++){
    int r1=fd(b[i].st),r2=fd(b[i].en);
    if (r1!=r2){t++;f[r1]=f[r2]=t;v[t]=b[i].v;add(t,r2);add(t,r1);if (t==2*n-1) break;}
  }
  for (int i=1;i<=n;i++) if (!vis[i]) dfs(fd(i));
  for (int i=1;i<=top;i++){
    int t=q[i];
    if (t<=n)insert(1,n,root[i],root[i-1],h[t]);
    else{
      root[i]=root[i-1];
      if (!bg[t]) bg[t]=i;else ed[t]=i;
    }
  }
}
int find(int x,int v){
 for(int i=17;i>=0;i--)if(deep[x]>=(1<<i)&&mx[x][i]<=v)x=fa[x][i];
 return x;
}
int query(int l,int r,int x,int y,int rk){
  if(l==r)return l;
  int mid=(l+r)>>1;
  if(sum[ls[y]]-sum[ls[x]]>=rk)return query(l,mid,ls[x],ls[y],rk);
  else return query(mid+1,r,rs[x],rs[y],rk-sum[ls[y]]+sum[ls[x]]);
}  
void work(){
 int x,v,k;
 for(int i=1;i<=p;i++){
   v=read();x=read();k=read();if(ans!=-1)v^=ans,x^=ans,k^=ans;
   int t=find(v,x);
   int l=root[bg[t]],r=root[ed[t]];
   if(sum[r]-sum[l]<k) ans=-1;
   else ans=s[query(1,n,l,r,sum[r]-sum[l]-k+1)];
   printf("%d\n",ans);
  }
}
int main(){
  n=read();m=read();p=read();
  for(int i=1;i<=n;i++) h[i]=read(),s[i]=h[i];
  sort(s+1,s+n+1);
  for(int i=1;i<=n;i++) h[i]=lower_bound(s+1,s+n+1,h[i])-s;
  for(int i=1;i<=n*2;i++) f[i]=i;
  for (int i=1;i<=m;i++) b[i].st=read(),b[i].en=read(),b[i].v=read(); 
  pre();work();
} 

                     
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值