您的位置:首页 > 其它

2017.10.6 BJOI2015 bzoj4336 骑士的旅行

2017-10-17 20:11 232 查看
 骑士的旅行

题目背景:

bzoj4336

分析:树链剖分 + 树状数组 + 权值线段树 + 二分

本场考试唯一一道我看了就知道怎么做的题······直接大数据结构暴力······

首先很显然的,需要

提取链上的信息 è 树链剖分

维护权值相关并且支持区间查询 è 主席树

维护单点修改 è 主席树换成树状数组 + 权值线段树

然后之后就是暴力的打代码就好了······按理说的最坏复杂度应该是O(nklog3n),不过因为树链剖分的常数很小,树状数组常数也小,线段树也不是全部都能跑满,再加上数据不是卡的太死,修改操作的O(log2n)复杂度也很稳,所以貌似跑的还比较快。

 

算法实现:

1、先树链剖分,方便过后提取链询问

2、然后将每一个骑士所在结点编号插入到对应的链剖过后的新编号上

3、针对每一次询问,提取出对应的区间所在的(log2n)个结点,然后进行k次二分,找到相应的k大值

4、针对每一次修稿,暴力在树状数组上修改logn次即可

Source:

/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>

inline char read() {
static const int IN_LEN = 1024 * 1024;
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 void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(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 == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(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 << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/

const int MAXN = 40000 + 10;
const int MAXX = 1000 + 3;

int n, m, q, x, y, k, cnt, ind, type;
int size[MAXN], father[MAXN], top[MAXN], son[MAXN], num[MAXN], dep[MAXN];
int f[MAXN], p[MAXN];
int root[MAXN];

std::vector<int> edge[MAXN];

struct node {
int cnt, left, right;
} tree[MAXN * 300];

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

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

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

inline void solve_tree() {
R(n);
for (int i = 1; i < n; ++i) R(x), R(y), add_edge(x, y);
dfs1(1, 0), dfs2(1, 1);
}

inline int lowbit(int i) {
return i & -i;
}

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

inline void insert(int i, int x, int k) {
for (; i <= n; i += lowbit(i)) insert(root[i], 1, MAXX, x, k);
}

inline void query(int i, int *a, int &cnt_a) {
for (; i; i -= lowbit(i)) a[++cnt_a] = root[i];
}

inline void solve_knight_pos() {
R(m);
for (int i = 1; i <= m; ++i) R(f[i]), R(p[i]), insert(num[p[i]], f[i], 1);
}

inline void solve(int u, int v) {
static int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
static int cnt_a, cnt_b;
cnt_a = cnt_b = 0;
int p = u, q = v;
while (top[p] != top[q]) {
dep[top[p]] > dep[top[q]] ?
(query(num[p], b, cnt_b), query(num[top[p]] - 1, a, cnt_a),
p = father[top[p]]) :
(query(num[q], b, cnt_b), query(num[top[q]] - 1, a, cnt_a),
q = father[top[q]]);
}
dep[p] > dep[q] ?
(query(num[p], b, cnt_b), query(num[q] - 1, a, cnt_a)) :
(query(num[q], b, cnt_b), query(num[p] - 1, a, cnt_a)) ;
int sum = 0, q_sum = 0;
for (int j = 1; j <= cnt_a; ++j) q_sum -= tree[a[j]].cnt;
for (int j = 1; j <= cnt_b; ++j) q_sum += tree[b[j]].cnt;
sum = q_sum, sum = std::min(sum, k);
if (sum == 0) W(-1);
for (int i = 0; i < sum; ++i) {
for (int j = 1; j <= cnt_a; ++j) c[j] = a[j];
for (int j = 1; j <= cnt_b; ++j) d[j] = b[j];
int l = 1, r = MAXX, k = q_sum - i;
while (l != r) {
int left_sum = 0, mid = l + r >> 1;
for (int j = 1; j <= cnt_b; ++j)
left_sum += tree[tree[d[j]].left].cnt;
for (int j = 1; j <= cnt_a; ++j)
left_sum -= tree[tree[c[j]].left].cnt;
if (left_sum >= k) {
for (int j = 1; j <= cnt_a; ++j) c[j] = tree[c[j]].left;
for (int j = 1; j <= cnt_b; ++j) d[j] = tree[d[j]].left;
r = mid;
} else {
for (int j = 1; j <= cnt_a; ++j) c[j] = tree[c[j]].right;
for (int j = 1; j <= cnt_b; ++j) d[j] = tree[d[j]].right;
l = mid + 1, k -= left_sum;
}
}
W(l), write_char(' ');
}
write_char('\n');
}

inline void solve_query() {
R(q), R(k);
while (q--) {
R(type), R(x), R(y);
switch (type) {
case 1: solve(x, y);
break ;
case 2: insert(num[p[x]], f[x], -1), p[x] = y;
insert(num[p[x]], f[x], 1);
break ;
case 3: insert(num[p[x]], f[x], -1), f[x] = y;
insert(num[p[x]], f[x], 1);
break ;
}
}
}

int main() {
solve_tree();
solve_knight_pos();
solve_query();
flush();
return 0;
}


 

 

 

 

 

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