CF916E
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的更多相关文章
- CF916E Jamie and Tree
CF916E Jamie and Tree 题意翻译 有一棵n个节点的有根树,标号为1-n,你需要维护以下三种操作 1.给定一个点v,将整颗树的根变为v 2.给定两个点u, v,将lca(u, v)所 ...
- CF916E Jamie and Tree 解题报告
CF916E Jamie and Tree 题意翻译 有一棵\(n\)个节点的有根树,标号为\(1-n\),你需要维护一下三种操作 1.给定一个点\(v\),将整颗树的根变为\(v\) 2.给定两个点 ...
- 题解 [CF916E] Jamie and Tree
题面 解析 这题考试时刚了四个小时. 结果还是爆零了 主要就是因为\(lca\)找伪了. 我们先考虑没有操作1,那就是裸的线段树. 在换了根以后,主要就是\(lca\)不好找(分类讨论伪了). 我们将 ...
- 【树剖】CF916E Jamie and Tree
好吧这其实应该不是树剖... 因为只要求子树就够了,dfs就好了 大概就是记录一个全局根root 多画几幅图会发现修改时x,y以root为根时的lca为以1为根时的lca(x,y),lca(root, ...
- From 7.8 To 7.14
From 7.8 To 7.14 大纲 学科 英语的话每天早上背单词, 争取每天做一篇完型, 一篇阅读, 一篇短文填空, 一篇改错, 一篇七选五??? 似乎太多了, 先试一下吧 语文的话, 尝试翻译一 ...
随机推荐
- java获取本机机器名
java获取本机机器名 InetAddress.getLocalHost().getHostName().toString();
- top命令行含义解析
快捷键“1”可以快速切换显示所有cpu的信息 快捷键‘x’可以高亮显示当前排序列 shift+方向键:可以快速切换排序的列 top -c 显示完整命令 load含义解释:http://www.ruan ...
- 一起talk C栗子吧(第一百回:C语言实例--使用信号量进行进程间同步与相互排斥一)
各位看官们.大家好,上一回中咱们说的是进程间同步与相互排斥的样例,这一回咱们说的样例是:使用信号量进行进程间同步与相互排斥. 闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,信号量是由著名 ...
- HTML大文件上传(博客迁移)
Html大文件上传:跳转 通过github和hexo进行搭建博客,主要是在没有网络的时候,可以本地访问,并支持markdown语法. 新博客地址:跳转
- UVALive3211- Now or later(二分+2-SAT)
题目链接 题意:有n架飞机.每架飞机都能够选择早着陆和晚着陆两种方式之中的一个,且必须选择一种. 任务就是安排全部飞机着陆时.相邻两个着陆时间间隔的最小值尽量大. 思路:用二分处理最小值尽量大.该题目 ...
- Dom对象的经常用法
Dom对象的经常用法: (1)getElementById() 查询给定ID属性值的元素,返回该元素的元素节点 1. 查询给定ID属性值的元素,返回该元素的元素节点.也称为元素对象. ...
- 【安卓笔记】抽屉式布局----DrawerLayout
效果例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- IE将開始屏蔽旧版ActiveX控件
微软IE团队上周宣布将在IE中屏蔽旧版本号的ActiveX控件以加强IE的安全性.首先会被禁用的旧版本号ActiveX控件包括: J2SE 1.4, 低于update 43 的版本号 J2SE 5.0 ...
- 【iOS系列】-iOS中内存管理
iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对 ...
- 追踪分布式Memcached默认的一致性hash算法
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255) ...