• 题解 P2633 【Count on a tree】


    题目链接

    我可能连普及水平都没有了.这题和[NOI2018]归程我都写炸了倍增LCA,以为主席树+树上差分(Kruskal重构树)写炸调了一晚上,总觉得还可以做些啥来抢救一下

    Solution Count on a tree

    题目大意:给定一颗树,每个点有一个点权,询问两点简单路径上第(k)小的点权.强制在线

    主席树,树上差分


    分析:看到第(k)小,我们想到什么?主席树

    很多树上的问题我们都可以从它在序列上的做法入手得到启示.在序列上,我们用权值线段树来保存元素,然后用可持久化的方式来保存序列.本质上我们是做了一个前缀和,因为权值线段树满足可加/减性,所以我们可以通过差分的方式来以区间([1,l - 1],[1,r])的权值线段树得到区间([l,r])权值线段树

    回到树上.我们从位置(1)开始做前缀和,那树上的"位置(1)"是什么?根节点.

    我们可以用保存每一个点到根节点路径上点权的权值线段树的前缀和.同样差分求解.对于两点(u,v)我们如果直接将它们的前缀和加起来是不行的,这样会多算一部分,而多算的那一部分就是它们(LCA)的前缀和以及(LCA)父亲的前缀和

    记点(u)到根节点路径点权的权值线段树的前缀和为(d[u]),那么最终我们要求的线段树就是(d[u] + d[v] - d[lca(u,v)] - d[faz[lca(u,v)]]) 可以一次(dfs)求出来

    套个树上倍增就好然后我愉快写挂了

    注意几点:

    • 此题(RE)多半不是你程序有什么大问题,而是(WA)了后由于强制在线(xor)出一个很大的点编号导致(RE)
    • 点权要离散化

    上蒟蒻的代码:

    #include <cstdio>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn = 1e5 + 100;
    int n,m,siz;
    vector<int> G[maxn];
    inline void addedge(int u,int v){
        G[u].push_back(v);
    }
    namespace ST{//主席树
        struct Node{
            int ls,rs,val;
        }tree[20000000];
        int root[maxn],cnt;
        inline int newnode(){return ++cnt;}
        inline int get(int x){return x == 0 ? 0 : tree[x].val;}
        inline void maintain(int root){tree[root].val = get(tree[root].ls) + get(tree[root].rs);}
        void add(int pos,int last,int &root,int l = 1,int r = siz){
            root = newnode();
            if(l == r){
                tree[root].val = tree[last].val + 1;
                return;
            }
            int mid = (l + r) >> 1;
            if(pos <= mid)add(pos,tree[last].ls,tree[root].ls,l,mid),tree[root].rs = tree[last].rs;
            else add(pos,tree[last].rs,tree[root].rs,mid + 1,r),tree[root].ls = tree[last].ls;
            maintain(root);
        }
        int query(int k,int add1,int add2,int del1,int del2,int l = 1,int r = siz){//差分
            if(l == r)return l;
            int mid = (l + r) >> 1,lsiz = get(tree[add1].ls) + get(tree[add2].ls) - get(tree[del1].ls) - get(tree[del2].ls);
            if(lsiz >= k)return query(k,tree[add1].ls,tree[add2].ls,tree[del1].ls,tree[del2].ls,l,mid);
            else return query(k - lsiz,tree[add1].rs,tree[add2].rs,tree[del1].rs,tree[del2].rs,mid + 1,r);
        }
    }
    const int maxdep = 25;
    int val[maxn],tmp[maxn],faz[maxn][maxdep + 1],dep[maxn];
    inline int rnk(int x){return lower_bound(tmp + 1,tmp + 1 + siz,x) - tmp;}//离散化
    void dfs(int u){//预处理
        for(int i = 1;i <= maxdep;i++)//倍增
            faz[u][i] = faz[faz[u][i - 1]][i - 1];
        ST::add(rnk(val[u]),ST::root[faz[u][0]],ST::root[u]);//由于是到根节点的前缀和,所以上一个版本是它的父亲
        for(int v : G[u])
            if(v != faz[u][0])
                faz[v][0] = u,dep[v] = dep[u] + 1,dfs(v);
    }
    inline int lca(int x,int y){//屡次写挂的LCA /kk
    	if(dep[x] > dep[y])swap(x,y);
    	for(int i = maxdep;i >= 0;i--)
    		if(dep[faz[y][i]] >= dep[x])
    			y = faz[y][i];
    	if(x == y)return x;
    	for(int i = maxdep;i >= 0;i--)
    		if(faz[x][i] != faz[y][i])
    			x = faz[x][i],y = faz[y][i];
    	return faz[x][0];
    }
    inline int getans(int u,int v,int k){//查询
        int l = lca(u,v);
        int pos = ST::query(k,ST::root[u],ST::root[v],ST::root[l],ST::root[faz[l][0]]);
        return tmp[pos];
    }
    int main(){
        scanf("%d %d",&n,&m);
        for(int i = 1;i <= n;i++)
            scanf("%d",&val[i]),tmp[i] = val[i];
        for(int a,b,i = 1;i <= n - 1;i++)
            scanf("%d %d",&a,&b),addedge(a,b),addedge(b,a);
        sort(tmp + 1,tmp + 1 + n),siz = unique(tmp + 1,tmp + 1 + n) - tmp - 1;//离散化
        dep[1] = 1;
        dfs(1);
        for(int lastans = 0,u,v,k,i = 1;i <= m;i++){
            scanf("%d %d %d",&u,&v,&k);
            printf("%d
    ",lastans = getans(u ^ lastans,v,k));
        }
        return 0;
    }
    
  • 相关阅读:
    《算法导论》读书笔记之第16章 贪心算法—活动选择问题
    C语言综合实验1—学生信息管理系统
    《算法导论》读书笔记之第15章 动态规划[总结]
    《算法导论》读书笔记之第11章 散列表
    模板类中定义list<T>::iterator iter在g++下不识别的解决办法
    C语言参考程序—无符号一位整数的四则运算
    《算法导论》读书笔记之第15章 动态规划—最优二叉查找树
    C语言综合实验2—长整数运算
    递归与尾递归总结
    《算法导论》读书笔记之第13章 红黑树
  • 原文地址:https://www.cnblogs.com/colazcy/p/11515059.html
Copyright © 2020-2023  润新知