zoukankan      html  css  js  c++  java
  • Count On A Tree 1

    题意

    给定一颗带点权有根树,每次查询两点之间路径上点权的第(k)大值


    解法

    求第(k)大?我们立刻想到主席树

    但是这是树上的问题,我们怎么进行操作呢?

    我们令每一颗主席树维护当前节点到根节点路径上的点权,那么每个点的主席树都由其父亲转移过来

    那么怎么查询(x,y)两个结点路径上的第(k)大点权呢?

    我们发现,想要获得(x,y)两个结点之间的路径对应的主席树,我们不能简单的提取(x,y)这两个点对应的主席树进行计算,因为我们会发现在之前的定义下,有一些多余的结点也算了进来:就是它们的(lca)到根节点路径上的点

    那么我们在线段树上二分时现在需要传入三个点所对应的主席树(x,y,LCA(x,y)),每次减去(LCA(x,y))上对应的结点

    但是这样我们会发现(LCA)也是(x,y)路径上的结点,而它被多减了一次

    所以我们还需要传入(LCA)的父亲参与线段树上二分

    把整个过程可以理解为一个前缀和的操作(本身主席树就利用的是前缀和的思想)

    最后成功的将(x,y)的路径提取出来


    代码

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    void fast_IO();
    
    const int N = 2e5 + 10;
    
    int n, m, t, lstans;
    
    int a[N], rt[N];
    
    int w[N], dep[N], f[N][25];
    
    int cap;
    int head[N], to[N], nxt[N];
    
    struct CTree {
    	
    	int sz;
    	int ls[N * 20], rs[N * 20], val[N * 20];
    	
    	void clear() { sz = 0; }
    	
    	int newnode() {
    		++sz;
    		ls[sz] = rs[sz] = val[sz] = 0;
    		return sz;	
    	}
    	
    	void mkchain(int &x, int y, int l, int r, int k) {
    		x = newnode();
    		ls[x] = ls[y], rs[x] = rs[y], val[x] = val[y] + 1;
    		if (l == r)	return;
    		int mid = l + r >> 1;
    		if (k <= mid)
    			mkchain(ls[x], ls[y], l, mid, k);
    		else
    			mkchain(rs[x], rs[y], mid + 1, r, k);
    	}
    	
    	int query(int x, int y, int z, int fz, int l, int r, int k) {
    		if (l == r)	return l;
    		int tot = val[ls[x]] - val[ls[z]] + val[ls[y]] - val[ls[fz]];
    		int mid = l + r >> 1;
    		if (k <= tot)
    			return query(ls[x], ls[y], ls[z], ls[fz], l, mid, k);
    		else	
    			return query(rs[x], rs[y], rs[z], rs[fz], mid + 1, r, k - tot);
    	}	
    	
    } tr;
    
    inline void add(int x, int y) {
    	to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
    }
    
    void discre() {
    	for (int i = 1; i <= n; ++i)	a[i] = w[i];
    	sort(a + 1, a + n + 1);
    	t = unique(a + 1, a + n + 1) - a - 1;
    	for (int i = 1; i <= n; ++i)
    		w[i] = lower_bound(a + 1, a + t + 1, w[i]) - a;
    }
    
    void DFS(int x, int fa) {
    	tr.mkchain(rt[x], rt[fa], 1, t, w[x]);
    	dep[x] = dep[fa] + 1, f[x][0] = fa;
    	for (int i = 1; i <= 20; ++i)	f[x][i] = f[f[x][i - 1]][i - 1];
    	for (int i = head[x]; i; i = nxt[i]) 
    		if (to[i] != fa)	DFS(to[i], x);
    }
    
    int LCA(int x, int y) {
    	if (dep[x] < dep[y])	swap(x, y);
    	for (int i = 20; i >= 0; --i)
    		if (dep[f[x][i]] >= dep[y])	x = f[x][i];
    	if (x == y)	return y;
    	for (int i = 20; i >= 0; --i)
    		if (f[x][i] != f[y][i])	x = f[x][i], y = f[y][i];
    	return f[x][0];
    }
    
    int main() {
    	
    	fast_IO();
    	
    	cin >> n >> m;
    	for (int i = 1; i <= n; ++i)	cin >> w[i];
    	
    	discre();
    	
    	int x, y;
    	for (int i = 1; i < n; ++i) {
    		cin >> x >> y;
    		add(x, y), add(y, x);
    	}
    	
    	tr.clear();
    	DFS(1, 0);
    	
    	int k, lca, flca;
    	for (int i = 1; i <= m; ++i) {
    		cin >> x >> y >> k;
    		x ^= lstans;
    		lca = LCA(x, y), flca = f[lca][0];
    		cout << (lstans = a[tr.query(rt[x], rt[y], rt[lca], rt[flca], 1, t, k)]) << endl;
    	}
    		
    	return 0;
    }
    
    void fast_IO() {
    	ios :: sync_with_stdio(false);
    	cin.tie(NULL), cout.tie(NULL);
    }
    
  • 相关阅读:
    教你发布Silverlight Bussiness Application(SQL Server 登录,局域网访问,以及使用ArcGIS Server服务需要注意的问题)
    ArcGIS API for Silverlight 使用GeometryService进行河流网格划分(三)
    ArcGIS Server 10.1发布要素服务时遇到的数据库注册问题总结 (二)
    使用ArcGIS API for Silverlight + Visifire绘制地图统计图
    使用Visifire+ArcGIS API for Silverlight实现Graphic信息的动态图表显示
    报个道
    Cgroups
    docker的文件系统
    go实现http服务
    linux调度器原理
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11470262.html
Copyright © 2011-2022 走看看