bzoj2588 Count on a tree

Count on a tree

题目背景:

bzoj2588

分析:这道题其实是一个不太难的数据结构题,用心想其实很容易发现只要在树上维护一个主席树就可以了,比较好想的就是直接在每一个每一个节点上,维护他到根的所有节点的权值,然后每一次查询的时候,直接像查询第k大数那样就可以了,每次传4个根进去就好了,uv,和lca以及lca的父亲然后每一次加上uvsum,减去lca以及lca的父亲的sum就好了,这样做应该是nlogn的。

Source

 

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<iostream>
#include<cctype>
#define rank asdakjwflkaw
using namespace std;

const int MAXN = 200000 + 10;
const int MAXM = 300000 + 10;
int father[MAXN], a[MAXN];
int cnt, top, size, tot, n, m, x, y, k;
int rank[MAXN], first[MAXN];
int fir[MAXM], line[MAXM];
int dep[MAXM], rmq[MAXM];
int low[MAXM], dp[MAXM][25];
int root[MAXM << 1];

struct node {
	int next, to;
} edge[MAXN];

struct data {
	int num, ori;
} w[MAXM];

struct chairman_tree {
	int left, right, sum;
} tree[MAXM << 4];

///*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
		if (c == '-') iosig = true;
	for (x = 0; isdigit(c); c = getchar())
		x = (x << 3) + (x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

inline bool comp(const data &a, const data &b) {
	return a.num < b.num;
}

inline void create(int x, int y) {
	tot++;
	edge[tot].next = first[x];
	first[x] = tot;
	edge[tot].to = y;
}

inline void read() {
	R(n), R(m);
	for (int i = 1; i <= n; ++i) R(w[i].num), w[i].ori = i;
	for (int i = 1; i < n; ++i) R(x), R(y), create(x,y), create(y,x);
}

inline void dfs1(int cur, int fa) {
	father[cur] = fa, fir[cur] = ++cnt, dep[cur] = dep[fa] + 1;
	line[cnt] = cur, rmq[cnt] = dep[cur];
	for (int p = first[cur]; p; p = edge[p].next)
		if (edge[p].to != fa) {
			dfs1(edge[p].to,cur);
			cnt++, line[cnt] = cur, rmq[cnt] = dep[cur];
		}
}

inline void preset(int n) {
	low[0] = -1;
	for (int i = 1; i <= n; ++i) {
		if ((i & (i - 1)) == 0) low[i] = low[i - 1] + 1;
		else low[i] = low[i - 1];
		dp[i][0] = i;
	}
	for (int j = 1; j <= low[n]; ++j)
		for (int i = 1; i + (1 << j) < n; ++i)
			if (rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]])
				dp[i][j] = dp[i][j - 1];
			else dp[i][j] = dp[i + (1 << (j - 1))][j - 1];
}

inline int get_lca(int x, int y) {
	if (x > y) swap(x, y);
	int j = low[y - x + 1];
	if (rmq[dp[x][j]] < rmq[dp[y - (1 << j) + 1][j]]) return line[dp[x][j]];
	else return line[dp[y - (1 << j) + 1][j]];
}

inline void pre_chairman_tree() {
	sort(w + 1, w + n + 1, comp);
	top = 1;
	a[w[1].ori] = 1;
	rank[1] = w[1].num;
	for (int i = 2; i <= n; ++i) {
		if (w[i].num != w[i - 1].num) {
			top++, rank[top] = w[i].num;
		}
		a[w[i].ori] = top;
	}
}

inline void build(int &x, int &num, int l, int r) {
	size++, tree[size] = tree[x], x = size, tree[x].sum++;
	if (l == r) return ;
	int mid = l + r >> 1;
	if (num <= mid) build(tree[x].left, num, l, mid);
	else build(tree[x].right, num, mid + 1, r);
}

inline void dfs2(int cur, int fa) {
	root[cur] = root[fa], build(root[cur], a[cur], 1, top);
	for (int p = first[cur]; p; p = edge[p].next)
		if (edge[p].to != fa) dfs2(edge[p].to,cur);
}

int query(int x, int y, int z, int w, int l, int r, int k) {
	if(l==r) return l;
	int temp = tree[tree[x].left].sum + tree[tree[y].left].sum
			 - tree[tree[z].left].sum - tree[tree[w].left].sum;
	int mid = l + r >> 1;
	if (temp >= k) return query(tree[x].left, tree[y].left, tree[z].left, 
								tree[w].left, l, mid, k);
	else return query(tree[x].right, tree[y].right, tree[z].right
								   , tree[w].right, mid + 1, r, k - temp);
}

void work() {
	int pre = 0;
	for(int i = 1; i <= m; ++i) {
		R(x), R(y), R(k), x ^= pre;
		int z = get_lca(fir[x], fir[y]);
		pre = rank[query(root[x], root[y], root[z], root[father[z]], 1, top, k)];
		cout << pre;
		if (i != m) cout << '\n';
	}
}

int main() {
	read();
	dfs1(1, 0);
	preset(cnt);
	pre_chairman_tree();
	dfs2(1, 0);
	work();
	return 0;
}

但是今天我做的时候比较尴尬,我完全忘了自己做过类似的题,直接强行发现可以强行进行树链剖分,然后每一次直接强行把路径上的每一条链剖出来,然后把底部的根和顶部根存储下来,因为最多有log个,所以可以每一次强行枚举每一个根累加上底部的每一个根的左儿子的sum,然后减去顶部每一个根的左儿子的sum,然后每一次强行判断全部的根变为左儿子或者是右儿子即可,不过这样应该是nlog2n的,但是我好像并没有T掉,反而因此发现我新的方法跑的比之前还要快了······然后我发现我彻底不明白了为什么自己log2n的算法,为什么会比log的还要快那么多······(可能是因为我之前的代码太丑了吧······)

Source

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#include <iostream>
#include <cctype>
#include <vector>

inline char read() {
	static const int IN_LEN = 1048576;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

///*
template<class T>
inline bool R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return false;
		if (c == '-') iosig = true;
	}
	for (x = 0; isdigit(c); c = read())
		x = (x << 3) + (x << 1) + (c ^ '0');
	if (iosig) x = -x;
	return true;
}
//*/

const int OUT_LEN = 1048576;
char obuf[OUT_LEN], *oh = obuf;

inline void writechar(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}

template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (!x) writechar(48);
	else {
		if (x < 0) writechar('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) writechar(buf[cnt--]);
	}
}

inline void flush() { fwrite(obuf, 1, oh - obuf, stdout); }

/*
template<class T>
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
		if (c == '-') iosig = true;
	for (x = 0; isdigit(c); c = getchar())
		x = (x << 3) + (x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int MAXN = 100000 + 10;
int rank[MAXN], root[MAXN];
int val[MAXN], son[MAXN], num[MAXN], father[MAXN];
int size[MAXN], dep[MAXN], top[MAXN], pos[MAXN], w[MAXN];
std::vector<int> edge[MAXN];
int ccnt, n, m, x, y, k, ind, cnt;

struct data {
	int num, ori;
	inline bool operator < (const data &a) const {
		return num < a.num;
	}
} a[MAXN];

struct Tree {
	int left, right, sum;
} tree[MAXN * 40];

inline void add_edge(int x, int y) {
	edge[x].push_back(y);
	edge[y].push_back(x);
}

inline void read_in() {
	R(n), R(m);
	for (int i = 1; i <= n; ++i) R(a[i].num), a[i].ori = i;
	for (int i = 1; i < n; ++i) R(x), R(y), add_edge(x, y);
}

inline void apart() {
	std::sort(a + 1, a + n + 1);
	int i = 1, j;
	while (i <= n) {
		j = i;
		while (a[i].num == a[i + 1].num) {
			if (i == n) break;
			++i;
		}
		ccnt++, rank[ccnt] = a[i].num;
		for (int k = j; k <= i; ++k) val[a[k].ori] = ccnt;
		++i;
	}
}

inline void dfs1(int cur, int fa) {
	father[cur] = fa, size[cur] = 1, dep[cur] = dep[fa] + 1;
	for (int p = 0; p < edge[cur].size(); ++p)
		if (edge[cur][p] != fa) {
			dfs1(edge[cur][p], cur);
			size[cur] += size[edge[cur][p]];
			if (!son[cur] || size[edge[cur][p]] > size[son[cur]])
				son[cur] = edge[cur][p];
		}
}

inline void dfs2(int cur, int tp) {
	top[cur] = tp, num[cur] = ++ind, pos[ind] = cur, w[ind] = val[cur];
	if (son[cur]) dfs2(son[cur], tp);
	for (int p = 0; p < edge[cur].size(); ++p)
		if (!num[edge[cur][p]]) dfs2(edge[cur][p], edge[cur][p]);
}

inline void build_tree(int x, int &cur, int l, int r, int num) {
	tree[++cnt] = tree[x], cur = cnt, tree[cur].sum++;
	if (l == r) return;
	int mid = l + r >> 1;
	if (num <= mid) build_tree(tree[x].left, tree[cur].left, l, mid, num);
	else build_tree(tree[x].right, tree[cur].right, mid + 1, r, num);
}

int root_r[MAXN], size_r;
int root_l[MAXN], size_l;
inline int solve_query(int u, int v, int k) {
	int p = u, q = v;
	size_l = size_r = 0;
	while (top[u] != top[v]) {
		if (dep[top[u]] > dep[top[v]]) {
			root_l[++size_l] = root[num[top[u]] - 1];
			root_r[++size_r] = root[num[u]];
			u = father[top[u]];
		} else {
			root_l[++size_l] = root[num[top[v]] - 1];
			root_r[++size_r] = root[num[v]];
			v = father[top[v]];
		}
	}
	if (dep[u] < dep[v]) {
		root_l[++size_l] = root[num[u] - 1];
		root_r[++size_r] = root[num[v]];
	} else {
		root_l[++size_l] = root[num[v] - 1];
		root_r[++size_r] = root[num[u]];
	}
	int l = 1, r = ccnt;
	while (true) {
		if (l == r) return l;
		int mid = l + r >> 1;
		int sum_r = 0, sum_l = 0;
		for (int i = 1; i <= size_r; ++i) {
			sum_r += tree[tree[root_r[i]].left].sum;
			sum_l += tree[tree[root_l[i]].left].sum; 
		}
		int temp = sum_r - sum_l;
		if (temp >= k) {
			r = mid;
			for (int i = 1; i <= size_r; ++i) {
				root_r[i] = tree[root_r[i]].left;
				root_l[i] = tree[root_l[i]].left;
			}
		} else {
			l = mid + 1, k -= temp;
			for (int i = 1; i <= size_r; ++i) {
				root_r[i] = tree[root_r[i]].right;
				root_l[i] = tree[root_l[i]].right;
			}
		}
	}
}

int last;
inline void solve() {
	root[0] = 0;
	for (int i = 1; i <= ind; ++i) 
		build_tree(root[i - 1], root[i], 1, ccnt, w[i]);
	for (int i = 1; i <= m; ++i) {
		R(x), R(y), R(k), x ^= last;
		last = rank[solve_query(x, y, k)];
		W(last);
		if (i != m) writechar('\n');
	}
}

int main() {
//	freopen("in.in", "r", stdin);
	read_in();
	apart();
	dfs1(1, 0);
	dfs2(1, 1);
	solve();
	flush();
	return 0;	
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值