您的位置:首页 > 理论基础 > 计算机网络

bzoj4538: [HNOI2016]网络

2016-09-19 21:46 405 查看
题目链接

维护一颗树。在每个时刻,有以下三个操作。

(u,v,w): 向从u至v的路径上添加一个重要度为w的任务。

(t): 取消t时刻添加的任务。

(x) 查询所有未覆盖x节点的任务中重要度的最大值。

这道题有诸多做法,然而我直接当做树链剖分练习题来做了。

对于这样的三个操作,我们可以考虑用树剖来维护。查询不覆盖某节点的任务难以直接用线段树实现,我们可以反其道而行之,将操作1中的覆盖区间取补集,查询时输出x点被1操作覆盖的任务的最大值。这样的操作与原操作等价。考虑到一个节点会被多个任务覆盖或取消覆盖,可以向线段树的每个节点套一个大根堆,查询时对所有覆盖该点的线段的堆顶元素取max。三个树套在一起,时间复杂度为O(Nlog3N),空间复杂度为O(Nlog2N)

这题还可以用kdtree,点分治,二分+树链交等各种优雅的做法秒杀,然而我一个都不会。

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

struct event {
int val, id;
inline bool operator < (const event &rhs) const {
return val < rhs.val;
}
}tmpe;

typedef priority_queue<event> heap;
const int N = 100010;

int n, m;
vector<int> E
;

int size
, dep
, fa
, son
, top
, seg
, segCnt;

void dfs1(int u, int f) {
size[u] = 1; dep[u] = dep[fa[u] = f] + 1;
for (int i = 0; i < E[u].size(); i++) {
int &v = E[u][i];
if (v == f) continue;
dfs1(v, u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}

void dfs2(int u, int tp) {
top[u] = tp; seg[u] = ++segCnt;
if (! son[u]) return;
dfs2(son[u], tp);
for (int i = 0; i < E[u].size(); i++) {
int &v = E[u][i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}

inline void initTree() {
dfs1(1, 0); dfs2(1, 1);
}

bool disable[N<<1];
heap hp[N<<1];
#define g(l, r) (l + r | l != r)
#define o g(l, r)
#define ls g(l, mid)
#define rs g(mid + 1, r)
int gl, gr, gans;
#define cg(l, r) gl = l; \
gr = r
#define H hp[o]

void ins(int l, int r) {
if (gl > gr) return;
if (gl <= l && r <= gr) {
H.push(tmpe); return;
}
int mid = l + r >> 1;
if (gl <= mid) ins(l, mid);
if (mid + 1 <= gr) ins(mid + 1, r);
}

void query(int l, int r) {
while (! H.empty() && disable[H.top().id]) H.pop();
if (! H.empty()) gans = max(gans, H.top().val);
if (l == r) return;
int mid = l + r >> 1;
if (gl <= mid) query(l, mid);
else query(mid + 1, r);
}

event cf[110<<1];
inline void Insert(int u, int v) {
int ecnt = 0;
int f1 = top[u], f2 = top[v];
while (f1 != f2) {
if (dep[f1] < dep[f2]) {
swap(f1, f2); swap(u, v);
}
cf[++ecnt] = (event){seg[f1], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};
u = fa[f1], f1 = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
cf[++ecnt] = (event){seg[v], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};

//O(logNlog(logN))差分求补集
sort(cf + 1, cf + 1 + ecnt);
int sum = 0, left = 1;
for (int i = 1; i <= ecnt; i++) {
if (sum == 0) {
cg(left, cf[i].val - 1); ins(1, n);
sum += cf[i].id;
} else {
sum += cf[i].id;
if (sum == 0) left = cf[i].val;
}
}
cg(left, n); ins(1, n);
}

inline void Query(int u) {
gans = -1;
cg(seg[u], seg[u]); query(1, n);
printf("%d\n", gans);
}

inline void work() {
for (int id = 1; id <= m; id++) {
int type; scanf("%d", &type);
if (type == 0) {
int a, b, v; scanf("%d%d%d", &a, &b, &v);
tmpe = (event){v, id};
Insert(a, b);
}
if (type == 1) {
int t; scanf("%d", &t);
disable[t] = true;
}
if (type == 2) {
int x; scanf("%d", &x);
Query(x);
}
}
}

int main() {
cin >> n >> m;
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
E[u].push_back(v); E[v].push_back(u);
}
initTree();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息