您的位置:首页 > 其它

bzoj2588 Count on a tree

2017-03-22 11:42 204 查看
Count on a tree

题目背景:

bzoj2588

分析:这道题其实是一个不太难的数据结构题,用心想其实很容易发现只要在树上维护一个主席树就可以了,比较好想的就是直接在每一个每一个节点上,维护他到根的所有节点的权值,然后每一次查询的时候,直接像查询第k大数那样就可以了,每次传4个根进去就好了,u,v,和lca以及lca的父亲然后每一次加上u,v的sum,减去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
; ++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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: