Codeforces 916E 简要题解Description

Description

有一棵n个点的树,每个节点上有一个权值wi,最开始根为1号点.现在有3种类型的操作:

1 root, 表示将根设为root.

2 u v x, 设u, v的最近公共祖先为p, 将p的子树中的所有点的权值加上x.

3 u, 查询u的子树中的所有点的权值和.

对于每个3操作,输出答案.

n <= 500000

Solution

​ 考虑不能直接换根. 换了根就完了

​ 那么我们考虑另一种办法,就是如何保持原来树的形态的情况下模拟出各个根的情况。

​ 那么考虑分类讨论,如果根不在一个节点的子树中,那么直接操作就可以。否则就对整棵树进行操作, 然后再减去多余的一部分就可以。

​ 新树中的LCA(u, v) 就是LCA(u, v), LCA(u, root), LCA(v, root)深度最大的点, 因为它最接近新根。

​ 然后直接操作就可以了

​ 考虑一些细节: 如果根是当前操作点就直接对整棵树进行操作即可。

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
const int BUF_SIZE = (int)1e6 + 10;
struct fastIO {
char buf[BUF_SIZE], buf1[BUF_SIZE];
int cur, cur1;
FILE *in, *out;
fastIO() {
cur = BUF_SIZE, in = stdin, out = stdout;
cur1 = 0;
}
inline char getchar() {
if(cur == BUF_SIZE) fread(buf, BUF_SIZE, 1, in), cur = 0;
return *(buf + (cur++));
}
inline void putchar(char ch) {
*(buf1 + (cur1++)) = ch;
if (cur1 == BUF_SIZE) fwrite(buf1, BUF_SIZE, 1, out), cur1 = 0;
}
inline int flush() {
if (cur1 > 0) fwrite(buf1, cur1, 1, out);
return cur1 = 0;
}
}IO;
#define getchar IO.getchar
#define putchar IO.putchar
LL read() {
char ch = getchar();
LL x = 0, flag = 1;
for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(LL x) {
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
void putString(char s[], char EndChar = '\n') {
rep(i, 0, strlen(s) - 1) putchar(*(s + i));
if(~EndChar) putchar(EndChar);
} #define Maxn 500009
struct edge {
int nxt, to;
}g[Maxn << 1];
int head[Maxn], e;
int a[Maxn];
int n, q, size[Maxn], dep[Maxn], son[Maxn], dfn[Maxn], efn[Maxn], _index, fa[Maxn], top[Maxn];
namespace INIT {
inline void add(int u, int v) {
g[++e] = {head[u], v}, head[u] = e;
g[++e] = {head[v], u}, head[v] = e;
}
void dfs_init(int u, int f) {
dep[u] = dep[f] + 1, size[u] = 1;
fa[u] = f;
for(int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if(v != f) {
dfs_init(v, u);
size[u] += size[v];
if(son[u] == -1 || size[son[u]] < size[v]) son[u] = v;
}
}
}
void dfs_son(int u, int _top) {
efn[++_index] = u, dfn[u] = _index;
top[u] = _top;
if(~son[u]) dfs_son(son[u], _top); else return;
for(int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if(v != fa[u] && v != son[u]) dfs_son(v, v);
}
}
void Main() {
clar(head, -1), clar(son, -1);
n = read(), q = read();
rep(i, 1, n) a[i] = read();
rep(i, 1, n - 1) add(read(), read());
dfs_init(1, 0);
dfs_son(1, 1);
}
}
namespace SGMT_tree {
LL tree[Maxn * 8], add[Maxn * 8];
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
inline void pushup(int rt) { tree[rt] = tree[lc(rt)] + tree[rc(rt)]; }
inline void pushdown(int rt, int l, int r) {
int mid = (l + r) >> 1;
if(add[rt]) {
tree[lc(rt)] += (mid - l + 1ll) * add[rt];
add[lc(rt)] += add[rt];
tree[rc(rt)] += (r - mid * 1ll) * add[rt];
add[rc(rt)] += add[rt];
add[rt] = 0;
}
}
void build(int rt, int l, int r) {
add[rt] = 0;
if(l == r) { tree[rt] = a[efn[l]]; return ; }
int mid = (l + r) >> 1;
build(ls), build(rs);
pushup(rt);
}
void modify(int rt, int l, int r, int x, int y, int val) {
if(x <= l && r <= y) {
tree[rt] += val * 1ll * (r - l + 1), add[rt] += (LL)val;
return;
}
int mid = (l + r) >> 1;
pushdown(rt, l, r);
if(y <= mid) modify(ls, x, y, val);
else if(x >= mid + 1) modify(rs, x, y, val);
else modify(ls, x, y, val), modify(rs, x, y, val);
pushup(rt);
}
LL query(int rt, int l, int r, int x, int y) {
if(x <= l && r <= y) return tree[rt];
int mid = (l + r) >> 1; LL res = 0;
pushdown(rt, l, r);
if(x <= mid) res = res + query(ls, x, y);
if(y >= mid + 1) res = res + query(rs, x, y);
return res;
}
}
namespace SOLVE {
int root;
int LCA(int u, int v) {
while(top[u] != top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int lca_New(int u, int v) {
int lca1 = LCA(u, v), lca2 = LCA(u, root), lca3 = LCA(v, root);
int Ans = INT_MIN, Ans1 = -1;
if(dep[lca1] > Ans) { Ans = dep[lca1]; Ans1 = lca1; }
if(dep[lca2] > Ans) { Ans = dep[lca2]; Ans1 = lca2; }
if(dep[lca3] > Ans) { Ans = dep[lca3]; Ans1 = lca3; }
assert(Ans1 != -1);
return Ans1;
}
int ninrt(int u) {
if(root == u) return 0;
if(dfn[root] < dfn[u] || dfn[root] > dfn[u] + size[u] - 1) return 0;
return 1;
}
int Find(int u, int anc) {
while(top[u] != top[anc]) {
if(fa[top[u]] == anc) return top[u];
u = fa[top[u]];
}
return son[anc];
}
void Main() {
root = 1;
SGMT_tree :: build(1, 1, n);
rep(i, 1, q) {
int opt = read();
if(opt == 1) root = read();
if(opt == 2) {
int u = read(), v = read(), val = read(), lca = lca_New(u, v);
if(lca == root) SGMT_tree :: modify(1, 1, n, 1, n, val);
else if(ninrt(lca)) {
int RootOther = Find(root, lca);
SGMT_tree :: modify(1, 1, n, 1, n, val);
SGMT_tree :: modify(1, 1, n, dfn[RootOther], dfn[RootOther] + size[RootOther] - 1, -val);
}else SGMT_tree :: modify(1, 1, n, dfn[lca], dfn[lca] + size[lca] - 1, val);
}
if(opt == 3) {
int u = read(); LL res = 0;
if(u == root) res = SGMT_tree :: query(1, 1, n, 1, n);
else if(ninrt(u)) {
int RootOther = Find(root, u);
res += SGMT_tree :: query(1, 1, n, 1, n);
res -= SGMT_tree :: query(1, 1, n, dfn[RootOther], dfn[RootOther] + size[RootOther] - 1);
}else res += SGMT_tree :: query(1, 1, n, dfn[u], dfn[u] + size[u] - 1);
write(res), putchar('\n');
}
}
}
}
int main() {
#ifdef Qrsikno
freopen("CF916E.in", "r", stdin);
freopen("CF916E.out", "w", stdout);
#endif
INIT :: Main();
SOLVE :: Main();
#ifdef Qrsikno
debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return IO.flush();
}

CF916E的更多相关文章

  1. CF916E Jamie and Tree

    CF916E Jamie and Tree 题意翻译 有一棵n个节点的有根树,标号为1-n,你需要维护以下三种操作 1.给定一个点v,将整颗树的根变为v 2.给定两个点u, v,将lca(u, v)所 ...

  2. CF916E Jamie and Tree 解题报告

    CF916E Jamie and Tree 题意翻译 有一棵\(n\)个节点的有根树,标号为\(1-n\),你需要维护一下三种操作 1.给定一个点\(v\),将整颗树的根变为\(v\) 2.给定两个点 ...

  3. 题解 [CF916E] Jamie and Tree

    题面 解析 这题考试时刚了四个小时. 结果还是爆零了 主要就是因为\(lca\)找伪了. 我们先考虑没有操作1,那就是裸的线段树. 在换了根以后,主要就是\(lca\)不好找(分类讨论伪了). 我们将 ...

  4. 【树剖】CF916E Jamie and Tree

    好吧这其实应该不是树剖... 因为只要求子树就够了,dfs就好了 大概就是记录一个全局根root 多画几幅图会发现修改时x,y以root为根时的lca为以1为根时的lca(x,y),lca(root, ...

  5. From 7.8 To 7.14

    From 7.8 To 7.14 大纲 学科 英语的话每天早上背单词, 争取每天做一篇完型, 一篇阅读, 一篇短文填空, 一篇改错, 一篇七选五??? 似乎太多了, 先试一下吧 语文的话, 尝试翻译一 ...

随机推荐

  1. Failed to execute 'toDataURL' on 'HTMLCanvasElement,在canvas.toDataURL()执行时候报错解决方案

    添加跨域条件   crossorigin="anonymous" [Redirect at origin 'http://xxx.xx.com' has been blocked ...

  2. SqlSugar最容易使用的ORM

    SqlSugar官网

  3. System表空间大小有10Gb,使用率达到95%,

    System表空间大小有10Gb,使用率达到95%,很好奇, 随后执行如下SQL,查看system表空间中使用空间最多的对象 SQL>SELECT * FROM DBA_SEGMENTS T W ...

  4. HDOJ 5416 CRB and Tree DFS

    CRB and Tree Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tot ...

  5. Meteor package.js

    在本章中,我们将学习如何创建自己的 meteor 包. 创建包 让我们添加在桌面上的新文件夹用来创建新的包.使用命令提示符窗口执行如下命令. C:\Users\Administrator\Deskto ...

  6. 247. Segment Tree Query II

    最后更新 二刷 09-Jna-2017 利用线段树进行区间查找,重点还是如何判断每一层的覆盖区间,和覆盖去见与当前NODE值域的关系. public class Solution { public i ...

  7. MAC上反编译android apk---apktool, dex2jar, jd-jui安装使用(含手动签名)

    前文 介绍了在Windows平台利用强大的APK-Multi-Tool进行反编译apk,修改smali源码后再回编译成apk的流程,最近受人之托,破解个apk,所幸的是所用到的这三个软件都是跨平台的, ...

  8. vue 定义全局函数

    方法一:main.js 注入 (1)在main.js中写入函数 Vue.prototype.changeData = function (){ alert('执行成功'); } (2)在所有组件里可调 ...

  9. UTF-8 GBK UTF8 GB2312之间的区别和关系

    UTF-8 GBK UTF8 GB2312之间的区别和关系     UTF-8:Unicode TransformationFormat-8bit,允许含BOM,但通常不含BOM.是用以解决国际上字符 ...

  10. hdoj2680 Choose the best route

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission ...