[TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]

按Dfs序逐个插入点,建立可持久化线段树,每次查询即可,具体详见代码。

不知道为什么,代码慢的要死,,

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <vector>

using namespace std;

template<const int _n,const int _m>
struct Edge
{
    struct Edge_base { int    to,next; }e[_m];
    int    cnt,p[_n];
    Edge() { clear(); }
    void    insert(const int x,const int y)
    { e[++cnt].to=y; e[cnt].next=p[x]; p[x]=cnt; return ; }
    int    start(const int x) { return p[x]; }
    void    clear() { cnt=1,memset(p,0,sizeof(p)); }
    Edge_base&    operator[](const int x) { return e[x]; }
};

Edge<110000,210000>    e;
vector<int>    vec;
int    n,q,tot,cnt;
int    a[110000],root[110000],Left[3100000],Right[3100000];
int    val[3100000],f[110000][19],depth[110000],lg2[110000];

void    Init()
{
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i<=n;++i)
            f[i][j]=f[f[i][j-1]][j-1];
    for(int i=2;i<=n;++i)lg2[i]=lg2[i>>1]+1;
    return ;
}

int    Lca(int A,int B)
{
    int    i,j;
    if(depth[A]<depth[B])swap(A,B);

    j=lg2[depth[A]];

    for(i=j;i>=0;--i)
    {
        if(depth[A]-(1<<i)>=depth[B])A=f[A][i];
    }

    if(A==B)return A;

    for(i=j;i>=0;--i)
    {
        if(f[A][i] && f[A][i]!=f[B][i])
            A=f[A][i],B=f[B][i];
    }
    return f[A][0];
}

void    Insert(const int l,const int r,const int root_l,int& root_r,const int d)
{
    val[root_r=++tot]=val[root_l]+1;
    if(l==r)return ;

    int mid=l+((r-l)>>1);
    if(d<=mid)
    {
        Right[root_r]=Right[root_l];
        Insert(l,mid,Left[root_l],Left[root_r],d);
    }
    else
    {
        Left[root_r]=Left[root_l];
        Insert(mid+1,r,Right[root_l],Right[root_r],d);
    }

    return ;
}

void    Dfs(const int S,const int fa)
{
    depth[S]=depth[fa]+1;
    f[S][0]=fa;

    a[S]=lower_bound(vec.begin(),vec.end(),a[S])-vec.begin()+1;
    Insert(1,vec.size(),root[fa],root[S],a[S]);

    for(int i=e.start(S);i;i=e[i].next)
    {
        if(e[i].to==fa)continue;
        Dfs(e[i].to,S);
    }
}    

int    kth(int l,int r,int r1,int r2,int r3,int r4,int d)
{
    while(true)
    {
        if(l==r)return l;
        int    mid=l+((r-l)>>1),temp;
        temp=val[Left[r1]]+val[Left[r2]]-val[Left[r3]]-val[Left[r4]];
        if(temp>=d)r=mid,r1=Left[r1],r2=Left[r2],r3=Left[r3],r4=Left[r4];
        else    l=mid+1,r1=Right[r1],r2=Right[r2],r3=Right[r3],r4=Right[r4],d=d-temp;
    }
    return 0;
}

int main()
{
    //freopen("in","r",stdin);

    int    i,x,y,op;

    vec.resize(110000);
    scanf("%d%d",&n,&q);

    for(i=1;i<=n;++i)
        scanf("%d",&a[i]),vec.push_back(a[i]);

    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());

    for(i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        e.insert(x,y);
        e.insert(y,x);
    }

    Dfs(1,0);
    Init();

    for(i=1;i<=q;++i)
    {
        scanf("%d%d%d",&op,&x,&y);
        int    temp=Lca(x,y),temp1;
        if(op==1)
            temp1=kth(1,vec.size(),
                    root[x],root[y],
                    root[temp],root[f[temp][0]],1);
        else if(op==2)
            temp1=kth(1,vec.size(),
                    root[x],root[y],
                    root[temp],root[f[temp][0]],
                    depth[x]+depth[y]-(depth[temp]<<1)+1);
        else    temp1=kth(1,vec.size(),
                root[x],root[y],
                root[temp],root[f[temp][0]],
                ((depth[x]+depth[y]-(depth[temp]<<1)+1)>>1)+1);

        printf("%d\n",vec[temp1-1]);
    }

    return 0;
}
时间: 2024-10-06 20:05:44

[TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]的相关文章

[tsA1490][2013中国国家集训队第二次作业]osu![概率dp+线段树+矩阵乘法]

这样的题解只能舔题解了,,,qaq 清橙资料里有.. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cmath> 6 #include <ctime> 7 #include <algorithm> 8 9 using namespace std; 10 11 struct Mat

[TS-A1486][2013中国国家集训队第二次作业]树[树的重心,点分治]

首先考虑暴力,可以枚举每两个点求lca进行计算,复杂度O(n^3logn),再考虑如果枚举每个点作为lca去枚举这个点的子树中的点复杂度会大幅下降,如果我们将每个点递归考虑,每次计算过这个点就把这个点删掉,那么如果每次删掉的点是当前子树的重心,枚举点对的复杂度就只有O(logn),对于每次查询答案在trie树中找到时间复杂度基本可视为O(1),最后在分治同时考虑“关键点”即可.总复杂度O(nlogn). 1 #include <iostream> 2 #include <algorith

[TS-A1489][2013中国国家集训队第二次作业]抽奖[概率dp]

概率dp第一题,开始根本没搞懂,后来看了09年汤可因论文才基本搞懂,关键就是递推的时候做差比较一下,考虑新加入的情况对期望值的贡献,然后推推公式(好像还是不太会推qaq...) 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 int n,m; 6 long double a[110000]; 7 8 long double POW(const long double t,int b) 9 { 10 long double r=1,

[tsA1491][2013中国国家集训队第二次作业]家族[并查集]

m方枚举,并查集O(1)维护,傻逼题,,被自己吓死搞成神题了... 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 struct tri { int x,y,z; bool operator<(const tri & temp)const { return z<temp.z; } }; 6 7 int n,m,k,Sum,Ans=0x7fffffff,a[1100],Size[1100],f[1100]; 8

Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB 总提交次数:196   AC次数:65   平均分:58.62 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2013中国国家集训队第二次作业 问题描述 给定一棵N个节点的树,每个点有一个权值,有M个询问(a,b,c)若a 为1,回答b到c路径上的最小权值,若a为2,回答b到c路径上的最大权值,若a为3,回答b到c路径上的所有权值的

&lt; &lt; &lt; 2013年国家集训队作业 &gt; &gt; &gt;

完成题数/总题数:  1道/37道 1.  A1504. Book(王迪): 数论+贪心   ★★☆

LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)

传送门 解题思路 快被调死的码农题,,,其实就是一个边权下放到点权的线段树+树剖. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; const int MAXN = 100005; const int inf = 0x3f3f3f3f; inline int rd(){

BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

题目大意:给出一个森林,每个节点都有一个权值.有若干加边操作,问两点之间路径上的第k小权值是多少. 思路:这题和COT1比较像,但是多了连接操作.这样就只能暴力合并连个树.启发式合并会保证时间复杂度不至于太大.然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本.之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值. CODE: #include <cstdio> #i

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ