dtoi1927 [ONTAK2010]Peaks加强版

题意:

给一个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;
}

原文地址:https://www.cnblogs.com/1124828077ccj/p/12239298.html

时间: 2024-08-02 16:24:19

dtoi1927 [ONTAK2010]Peaks加强版的相关文章

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 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

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,表

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

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

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.二

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) {

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

传送门:bzoj  bzoj wdnmd为什么加强版不是权限题原题却是啊 3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达