SP2666Solution
SrsgPrince_ · · 题解
QTREE4 - Query on a tree IV Solution
题面传送门:QTREE4 - Query on a tree IV。
推荐在 博客 中查看。
题目分析 Hint
题目中让我们进行两个操作:
-
C a将点a 的颜色反转。(黑变白,白变黑) -
A询问dist(a,b) 的最大值。a,b 点都必须为白色(a 与b 可以相同),显然如果树上仍存在白点,查询得到的值一定是个非负数。特别地,如果A操作时树上没有白点,输出They have disappeared.。
第一个操作是修改颜色,第二个操作是维护边权。考虑用树链剖分做。
思路 Solution
先对原树进行树链剖分,并且对重链建树。建树的方法很简单,先令
也就是如下代码。
inline void pushUp(int p, int lft, int rgt) {
int ls = tr[p].lson, rs = tr[p].rson, mid = (lft + rgt) >> 1;
tr[p].lmax = max(tr[ls].lmax, tr[rs].lmax + dep[rnk[mid + 1]] - dep[rnk[lft]]);
tr[p].rmax = max(tr[rs].rmax, tr[ls].rmax + dep[rnk[rgt]] - dep[rnk[mid]]);
tr[p].mx = max(max(tr[ls].mx, tr[rs].mx), tr[ls].rmax + tr[rs].lmax + dep[rnk[mid + 1]] - dep[rnk[mid]]);
}
接下来是修改点的颜色。修改一个点的颜色,要先修改当前点所在的重链线段树上进行修改。我们还是用一个堆来进行维护,可以
这里给出代码。
struct Edge {
int des, w;
};
vector<Edge> ed[maxn];
inline void dfs1(int x, int fth) {
siz[x] = 1, son[x] = -1;
for (auto y : ed[x]) {
if (y.des == fth) continue;
dep[y.des] = dep[x] + y.w, fa[y.des] = x, dfs1(y.des, x), siz[x] += siz[y.des];
if (son[x] < 0 || siz[y.des] > siz[son[x]]) son[x] = y.des;
}
}
inline void dfs2(int x, int tp) {
top[x] = tp, dfn[x] = ++tme, rnk[tme] = x, len[tp]++;
if (son[x] < 0) return;
dfs2(son[x], tp);
for (auto y : ed[x])
if (y.des != son[x] && y.des != fa[x])
dfs2(y.des, y.des);
}
struct Queue {
priority_queue<int> q1, q2;
inline void insert(int v) {if (v != -inf) q1.push(v);}
inline void erase(int v) {if (v != -inf) q2.push(v);}
inline int top() {
while (1) {
if (q1.empty()) return -inf;
if (q2.empty() || q1.top() != q2.top()) return q1.top();
q1.pop(), q2.pop();
}
}
}hp[maxn], res;
struct SegmentTree {
int lson, rson, lmax, rmax, mx;
} tr[maxn << 2];
int tot, rt[maxn];
// 这里不在重复 pushUp
inline void buildTree(int &p, int lft, int rgt) {
if (!p) p = ++tot;
if (lft == rgt) {
int x = rnk[lft];
for (auto y : ed[x]) {
if (y.des == fa[x] || y.des == son[x]) continue;
hp[x].insert(tr[rt[y.des]].lmax + dep[y.des] - dep[x]);
}
int mx1 = hp[x].top(); hp[x].erase(mx1);
int mx2 = hp[x].top(); hp[x].insert(mx1);
tr[p].lmax = tr[p].rmax = max(0, mx1), tr[p].mx = max(0, max(mx1, mx1 + mx2));
return;
}
int mid = (lft + rgt) >> 1;
buildTree(tr[p].lson, lft, mid), buildTree(tr[p].rson, mid + 1, rgt);
pushUp(p, lft, rgt);
}
inline void update(int p, int lft, int rgt, int id) {
if (lft == rgt) {
int x = rnk[lft];
int mx1 = hp[x].top(); hp[x].erase(mx1);
int mx2 = hp[x].top(); hp[x].insert(mx1);
if (col[x]) tr[p].lmax = tr[p].rmax = mx1, tr[p].mx = mx1 + mx2;
else tr[p].lmax = tr[p].rmax = max(0, mx1), tr[p].mx = max(0, max(mx1, mx1 + mx2));
return;
}
int mid = (lft + rgt) >> 1;
if (id <= mid) update(tr[p].lson, lft, mid, id);
else update(tr[p].rson, mid + 1, rgt, id);
pushUp(p, lft, rgt);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i < n; ++i) {
int u, v, w;
cin >> u >> v >> w;
ed[u].push_back({v, w});
ed[v].push_back({u, w});
}
dfs1(1, 0);
dfs2(1, 1);
for (int i = n; i; --i) {
int x = rnk[i];
if (x != top[x]) continue;
buildTree(rt[x], dfn[x], dfn[x] + len[x] - 1);
res.insert(tr[rt[x]].mx);
}
int m;
cin >> m;
int cnt = n;
while (m--) {
char ch;
cin >> ch;
if (ch == 'A') {
if (!cnt) cout << "They have disappeared.\n";
else cout << res.top() << "\n";
continue;
}
int x;
cin >> x;
col[x] ^= 1;
if (!col[x]) cnt++; else cnt--;
while (x) {
int tp = top[x];
res.erase(tr[rt[tp]].mx);
if (fa[tp]) hp[fa[tp]].erase(tr[rt[tp]].lmax + dep[tp] - dep[fa[tp]]);
update(rt[tp], dfn[tp], dfn[tp] + len[tp] - 1, dfn[x]);
if (fa[tp]) hp[fa[tp]].insert(tr[rt[tp]].lmax + dep[tp] - dep[fa[tp]]);
res.insert(tr[rt[tp]].mx);
x = fa[tp];
}
}
return 0;
}
也不知道为什么,我一开始 TLE 了两次,查过了所有的地方,都没问题,自造数据也没问题,最后不知道为什么改了输入输出,用了 cin/cout 然后就过了,快读快写反而没过,感觉是 putchar 在 SPOJ 里面寄了。