您的位置:首页 > 产品设计 > UI/UE

SPOJ1487. Query on a tree III 树链剖分+主席树

2014-04-07 07:06 134 查看
思路还是不太难的,只是我没想到。。。就是把点映射到数列上(就是剖分),然后求区间第K大(同一棵子树在数列上的位置总是连续的,所以把树链剖分看成一种特殊的hash)。

限制十分坑爹,在spoj上时间限制很紧,原先写主席树TLE了。后来去bzoj上提交,然后MLE了

(spoj空间256M,bzoj只有64M),只能不断地删掉没用的数组,然后重复用,所以代码写得很乱。不过在bzoj上AC之后,在spoj上竟然也AC了(我没有优化时间啊),果真坑爹。

感觉主席树的常数和空间都有点大,不知道需不需要学学划分树。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)
#define MEM(a) memset(a,0,sizeof(a))
#define mid ((l+r)>>1)
using namespace std;

const int MAXN = 100010;
const int MAXM = 2011110;
const int INF = 999999999;

int f[MAXN], son[MAXN], s[MAXN], h[MAXN], w[MAXN], v[MAXN];
int Time;
int root[MAXN], tr[MAXM], ch[MAXM][2];

struct edge
{
int u, next;
}e[MAXN << 1];
int head[MAXN], nume, numn;

inline void addedge(int x, int u)
{
e[++nume].u = u;
e[nume].next = head[x];
head[x] = nume;
}

void dfs1(int c)
{
s[c] = 1;
for (int i = head[c]; i; i = e[i].next)
{
if (e[i].u != f[c])
{
f[e[i].u] = c;
dfs1(e[i].u);
s[c] += s[e[i].u];
if (s[e[i].u] > s[son[c]]) son[c] = e[i].u;
}
}
}

void dfs2(int c, int tp)
{
h[c] = ++Time; w[Time] = c;
if (!son[c]) return;
dfs2(son[c], tp);
for (int i = head[c]; i; i = e[i].next) if (e[i].u != f[c] && e[i].u != son[c]) dfs2(e[i].u, e[i].u);
}

void build(int &cx, int &cy, int l, int r, int x)
{
if (!cx) cx = ++numn;
if (l == r)
{
tr[cx] = tr[cy] + 1;
return;
}
int m = mid;
tr[cx] = tr[cy] + 1;
if (x <= m)
{
build(ch[cx][0], ch[cy][0], l, m, x);
ch[cx][1] = ch[cy][1];
}
else
{
build(ch[cx][1], ch[cy][1], m + 1, r, x);
ch[cx][0] = ch[cy][0];
}
}

int query(int cx, int cy, int l, int r, int x)
{
int t = tr[ch[cx][0]] - tr[ch[cy][0]], m = mid;
if (l == r) return f[l];
if (t >= x) return query(ch[cx][0], ch[cy][0], l, m, x);
else return query(ch[cx][1], ch[cy][1], m + 1, r, x - t);
}

inline bool cmp(edge a, edge b) { return a.u < b.u; }

void work()
{
int i, j, n, t1, t2, t3, tx, ty, t, q, ans;
char c[100];
Time = nume = numn = 0;
scanf("%d", &n);
for (i = 1; i <= n; ++i)
{
scanf("%d", &v[i]);
e[i].u = v[i]; //离散化,借用边的数组(原先以为会MLE,所以代码写得很乱)
e[i].next = i;
}
sort(e + 1, e + n + 1, cmp);
for (i = 1; i <= n; ++i) v[e[i].next] = i;
for (i = 1; i < n; ++i)
{
scanf("%d%d", &t1, &t2);
addedge(t1, t2);
addedge(t2, t1);
}
scanf("%d", &q);
dfs1(1);
dfs2(1, 1);
for (i = 1; i <= n; ++i) f[v[i]] = i;//f数组用作v的反向索引,输出答案用到
for (i = 1; i <= n; ++i) build(root[i], root[i - 1], 1, n, v[w[i]]);//构树
for (i = 1; i <= q; ++i)
{
scanf("%d%d", &t1, &t2);
printf("%d\n", query(root[h[t1] + s[t1] - 1], root[h[t1] - 1], 1, n, t2));
}
}

int main()
{
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: