BZOJ-2588-Count-on-a-tree-SPOJ10628-LCA+主席树

描述

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。


分析

  • 想用树链剖分但是发现不知道怎么套主席树.
  • 正解是LCA, 只要维护根节点到每个结点u的权值线段树. 因为主席树求第k大支持减法, 所以最终答案就是线段树 u+v-lca(u,v)-fa[lca(u,v)] 中的第k大.
  • 调了半天发现在用lower_bound查找离散后的权值时, unique后的T数组有序不假但重复元素被挪到了数组尾, 所以不能再用原来的T+n+1作为二分查找的上界了.

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
using namespace std;

const int maxn = 100000 + 10;
const int maxm = 3000000;
const int DEP = 20;

vector
      
      
       
        G[maxn];

int n, m, tot, node_cnt;
int A[maxn], T[maxn];
int dep[maxn], fa[maxn][DEP];
int root[maxm], lc[maxm], rc[maxm], s[maxm];

#define M (L+R>>1)

void modify(int& x, int y, int L, int R, int d) {
	x = ++node_cnt;
	lc[x] = lc[y]; rc[x] = rc[y]; s[x] = s[y] + 1;
	if(L == R) return;
	if(d <= M) modify(lc[x], lc[y], L, M, d);
	else modify(rc[x], rc[y], M+1, R, d);
}

int query(int u, int v, int x, int y, int L, int R, int k) {
	if(L == R) return L;
	int ls = s[lc[u]] + s[lc[v]] - s[lc[x]] - s[lc[y]];
	if(k <= ls) return query(lc[u], lc[v], lc[x], lc[y], L, M, k);
	return query(rc[u], rc[v], rc[x], rc[y], M+1, R, k-ls);
}

void dfs(int u, int x) {
	dep[u] = dep[fa[u][0] = x] + 1;
	for(int i = 1; i < DEP; i++)
		if((1<
       
       
         <= dep[u]) fa[u][i] = fa[fa[u][i-1]][i-1]; else break; modify(root[u], root[x], 1, tot, A[u]); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(v != x) dfs(v, u); } } int lca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); int delta = dep[x]-dep[y]; for(int i = 0; i < DEP; i++) if(delta&(1< 
        
          = 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } int main() { int n, m; scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d", &A[i]); T[i] = A[i]; } sort(T+1, T+n+1); tot = unique(T+1, T+n+1) - T-1; for(int i = 1; i <= n; i++) A[i] = lower_bound(T+1, T+tot+1, A[i]) - T; for(int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, 0); int ans = 0; for(int i = 1; i <= m; i++) { int u, v, k; scanf("%d %d %d", &u, &v, &k); u ^= ans; int x = lca(u, v), y = fa[x][0]; ans = T[query(root[u], root[v], root[x], root[y], 1, tot, k)]; if(i < m) printf("%d\n", ans); else printf("%d", ans); } return 0; } 
         
       
      
      
     
     
    
    
   
   


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值