[bzoj3551][并查集][树上倍增][主席树]Peaks加强版

18 篇文章 0 订阅
14 篇文章 0 订阅

Description

在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,表示一组询问。v=v xor lastans,x=x xor lastans,k=k xor
lastans。如果lastans=-1则不变。

Output

对于每组询问,输出一个整数表示答案。

Sample Input

Sample Output

HINT

【数据范围】

N<=10^5, M,Q<=5*10^5,h_i,c,x<=10^9。

题解

这题超强,首先没样例差评,所以你得把强制在线去了然后找3545的样例测,3545还是权限题这题不是
3545我还是有点想法离线+并查集+线段树合并的,强制在线不会了。。
%啊
%到一个神奇的东西:Kruskal重构树
感谢popoqqq大爷的blog
简单来说,和LCT存边权差不多的道理。Kruskal建边的时候,不直接把两个连通块连起来,而是再拉一个点,给这个点一个点权,点权相当于边权。然后把这个点与这两个连通块的根连起来,于是这两个点之间的最大距离即为这两个点LCA的权值。
还有一些很不错的性质,比如这棵树是一棵二叉树,且满足大根堆的性质
这题的话,你可以预处理出来这棵重构树。然后对于一个点,把他向上倍增,找到一个最大且不大于给定的值的点,可以发现这个点的子树就是他所能到达的所有点。那么dfs序套主席树即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct edge{int x,y,c;}e[510000];
bool cmpx(edge n1,edge n2){return n1.c<n2.c;}
int col[410000],sum[410000];
struct LSnode{int y,p;}w[410000];int Rank[410000];
bool cmp(LSnode n1,LSnode n2){return n1.y<n2.y;}
struct trnode
{
    int lc,rc,c;
}tr[6110000];int trlen;
void add(int &now,int l,int r,int p)
{
    if(now==0)now=++trlen;
    tr[now].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(p<=mid)add(tr[now].lc,l,mid,p);
    else add(tr[now].rc,mid+1,r,p);
}
int rt[410000];
void merge(int &x,int y)
{
    if(x==0){x=y;return ;}
    if(y==0)return ;
    tr[x].c+=tr[y].c;
    merge(tr[x].lc,tr[y].lc);
    merge(tr[x].rc,tr[y].rc);
}
struct node
{
    int x,y,next;
}a[510000];int len,last[211000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,m,q,fa[210000],H[210000],cnt,bin[25];
int findfa(int x){return fa[x]!=x?fa[x]=findfa(fa[x]):fa[x];}
int f[210000][25],dep[210000];
int l[210000],r[210000],dfn;
void pre_tree_node(int x)
{
    for(int i=1;bin[i]<=dep[x];i++)f[x][i]=f[f[x][i-1]][i-1];
    if(col[x]!=0)add(rt[dfn+1],1,n,Rank[x]);
    merge(rt[dfn+1],rt[dfn]);
    l[x]=++dfn;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=f[x][0])
        {
            f[y][0]=x;
            dep[y]=dep[x]+1;
            pre_tree_node(y);
        }
    }
    r[x]=dfn;
}
int lastans,tt;
int findKth(int x,int y,int l,int r,int K)
{
    if(K>tr[y].c-tr[x].c)return -1;
    if(l==r)return w[l].y;
    int lcx=tr[x].lc,rcx=tr[x].rc,lcy=tr[y].lc,rcy=tr[y].rc;
    int c=tr[rcy].c-tr[rcx].c;
    int mid=(l+r)/2;
    if(K<=c)return findKth(rcx,rcy,mid+1,r,K);
    else return findKth(lcx,lcy,l,mid,K-c);
}
int sol(int u,int p,int k)
{
    int X=u;
    for(int i=22;i>=0;i--)
        if(bin[i]<dep[X] && sum[f[X][i]]<=p)X=f[X][i];
    return findKth(rt[l[X]-1],rt[r[X]],1,tt,k);
}
int main()
{
    bin[0]=1;
    for(int i=1;i<=22;i++)bin[i]=bin[i-1]*2;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){scanf("%d",&col[i]);w[i].y=col[i];w[i].p=i;}
    sort(w+1,w+1+n,cmp);
    tt=1;Rank[w[1].p]=1;
    for(int i=2;i<=n;i++)
    {
        tt++;
        Rank[w[i].p]=tt;
    }
    for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
    sort(e+1,e+1+m,cmpx);
    for(int i=1;i<=n;i++)fa[i]=i,H[i]=i;
    int cnt=n;
    for(int i=1;i<=m;i++)
    {
        int p=findfa(e[i].x),q=findfa(e[i].y);
        if(p!=q)
        {
            cnt++;
            ins(cnt,H[p]);ins(cnt,H[q]);sum[cnt]=e[i].c;
            fa[p]=q;H[q]=cnt;
        }
    }
    for(int i=1;i<=n;i++)
    {
        fa[i]=findfa(fa[i]);
        if(fa[i]==i)
        {
            int u=H[i];
            f[u][0]=0;dep[u]=1;pre_tree_node(u);
        }
    }
    lastans=0;
    while(q--)
    {
        int u,x,k;
        scanf("%d%d%d",&u,&x,&k);
        if(lastans!=-1)u^=lastans,x^=lastans,k^=lastans;
        lastans=sol(u,x,k);
        printf("%d\n",lastans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值