CF916E Jamie and Tree

题意翻译

有一棵n个节点的有根树,标号为1-n,你需要维护以下三种操作

1.给定一个点v,将整颗树的根变为v

2.给定两个点u, v,将lca(u, v)所在的子树都加上x

3.给定一个点v,你需要回答以v所在的子树的权值和

Translated by mangoyang


错误日志: 第一次 \(debug\) 是 \(jump\) 数组第二维开小了; 交了一次错了, 第二次没有特判修改/查询节点等于根的情况; 第三次 \(RE\) 又是数组开销了 。。。空间那么大我倒是把数组卡大点啊啊啊


Solution

树链剖分, 要求换根修改和查询

\(lca(u,v)\) 等于 \(lca(u, v)\ ,lca(u, root)\ ,lca(v, root)\) 里深度最大的点

修改和查询: 分三种情况考虑:

  1. 操作节点为根节点: 直接操作于整棵树
  2. 根节点在操作节点子树之外: 直接操作即可
  3. 根节点位于操作节点子树内: 利用容斥(最好画图看看)。设点 \(son\) 为从根节点到操作节点路径上的倒数第二个点,先整棵树更新, 再将 \(son\) 的子树减去更新值即可

    (又或者常数较大的先更新整棵树, 反过来减去操作节点的子树, 再更新操作节点这一个点)

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
typedef long long LL;
using namespace std;
LL RD(){
LL out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const LL maxn = 200019,INF = 1e9 + 19;
LL head[maxn],nume = 1;
struct Node{
LL v,dis,nxt;
}E[maxn << 2];
void add(LL u,LL v,LL dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
LL num, na;
LL dep[maxn], size[maxn], fa[maxn], wson[maxn], top[maxn], pos[maxn], ori[maxn], cnt;
LL v[maxn];
void dfs1(LL id, LL F){
size[id] = 1;
for(LL i = head[id];i;i = E[i].nxt){
LL v = E[i].v;
if(v == F)continue;
dep[v] = dep[id] + 1;
fa[v] = id;
dfs1(v, id);
size[id] += size[v];
if(size[v] > size[wson[id]])wson[id] = v;
}
}
void dfs2(LL id, LL TP){
pos[id] = ++cnt;
ori[cnt] = id;
top[id] = TP;
if(!wson[id])return ;
dfs2(wson[id], TP);
for(LL i = head[id];i;i = E[i].nxt){
LL v = E[i].v;
if(v == fa[id] || v == wson[id])continue;
dfs2(v, v);
}
}
#define lid (id << 1)
#define rid (id << 1) | 1
struct seg_tree{
LL l, r;
LL lazy, sum;
}tree[maxn << 2];
void pushup(LL id){tree[id].sum = tree[lid].sum + tree[rid].sum;}
void pushdown(LL id){
if(tree[id].lazy){
LL val = tree[id].lazy;
tree[lid].lazy += val;
tree[rid].lazy += val;
tree[lid].sum += (tree[lid].r - tree[lid].l + 1) * val;
tree[rid].sum += (tree[rid].r - tree[rid].l + 1) * val;
tree[id].lazy = 0;
}
}
void build(LL id, LL l, LL r){
tree[id].l = l, tree[id].r = r;
if(l == r){
tree[id].sum = v[ori[l]];
return ;
}
LL mid = (l + r) >> 1;
build(lid, l, mid), build(rid, mid + 1, r);
pushup(id);
}
void update(LL id, LL val, LL l, LL r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
tree[id].sum += (tree[id].r - tree[id].l + 1) * val;
tree[id].lazy += val;
return ;
}
LL mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l)update(rid, val, l, r);
else if(mid >= r)update(lid, val, l, r);
else update(lid, val, l, mid), update(rid, val, mid + 1, r);
pushup(id);
}
LL query(LL id, LL l ,LL r){
pushdown(id);
if(tree[id].l == l && tree[id].r == r){
return tree[id].sum;
}
LL mid = (tree[id].l + tree[id].r) >> 1;
if(mid < l)return query(rid, l, r);
else if(mid >= r)return query(lid, l, r);
else return query(lid, l, mid) + query(rid, mid + 1, r);
}
LL root = 1, jump[maxn][25];
void get_jump(){
for(LL i = 1;i <= num;i++)jump[i][0] = fa[i];
for(LL i = 1;i <= 19;i++){
for(LL j = 1;j <= num;j++){
jump[j][i] = jump[jump[j][i - 1]][i - 1];
}
}
}
LL LCA(LL x, LL y){
if(dep[x] < dep[y])swap(x, y);
for(LL i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
if(x == y)return x;
for(LL i = 19;i >= 0;i--)if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
return jump[x][0];
}
LL real_LCA(LL x, LL y){
LL lca1 = LCA(x, y), lca2 = LCA(x, root), lca3 = LCA(y, root);
LL lca = dep[lca1] > dep[lca2] ? lca1 : lca2;
return dep[lca] > dep[lca3] ? lca : lca3;
}
LL son_LCA(LL x, LL y = root){
if(dep[x] >= dep[y])return -1;
for(LL i = 19;i >= 0;i--)if(dep[jump[y][i]] >= dep[x] + 1)y = jump[y][i];
if(fa[y] == x)return y;
return -1;
}
void change_root(){root = RD();}
void uprange(){
LL x = RD(), y = RD(), val = RD();
LL lca = real_LCA(x, y);
if(lca == root){update(1, val, pos[1], pos[1] + size[1] - 1);return ;}
LL son = son_LCA(lca);
if(son == -1){update(1, val, pos[lca], pos[lca] + size[lca] - 1);return ;}
update(1, val, pos[1], pos[1] + size[1] - 1);
update(1,-val, pos[son], pos[son] + size[son] - 1);
}
void get_sum(){
LL x = RD();
if(x == root){printf("%lld\n", query(1, pos[1], pos[1] + size[1] - 1));return ;}
LL son = son_LCA(x);
if(son == -1){printf("%lld\n", query(1, pos[x], pos[x] + size[x] - 1));return ;}
printf("%lld\n", query(1, pos[1], pos[1] + size[1] - 1) - query(1, pos[son], pos[son] + size[son] - 1));
}
int main(){
num = RD();na = RD();
for(LL i = 1;i <= num;i++)v[i] = RD();
for(LL i = 1;i <= num - 1;i++){
LL u = RD(), v = RD();
add(u, v, 1), add(v, u, 1);
}
dep[1] = 1;
dfs1(1, -1), dfs2(1, 1);
build(1, 1, num);
get_jump();
for(LL i = 1;i <= na;i++){
LL cmd = RD();
if(cmd == 1)change_root();
else if(cmd == 2)uprange();
else get_sum();
}
return 0;
}

CF916E Jamie and Tree的更多相关文章

  1. CF916E Jamie and Tree 解题报告

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

  2. 题解 [CF916E] Jamie and Tree

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

  3. 【树剖】CF916E Jamie and Tree

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

  4. Codeforces 916E Jamie and Tree (换根讨论)

    题目链接  Jamie and Tree 题意  给定一棵树,现在有下列操作: $1$.把当前的根换成$v$:$2$.找到最小的同时包含$u$和$v$的子树,然后把这棵子树里面的所有点的值加$x$: ...

  5. codeforces 916E Jamie and Tree dfs序列化+线段树+LCA

    E. Jamie and Tree time limit per test 2.5 seconds memory limit per test 256 megabytes input standard ...

  6. CodeForces 916E Jamie and Tree(树链剖分+LCA)

    To your surprise, Jamie is the final boss! Ehehehe. Jamie has given you a tree with n vertices, numb ...

  7. Jamie and Tree CodeForces - 916E (换根)

    大意: n节点树, 每个点有权值, 三种操作: 1,换根. 2, lca(u,v)的子树权值全部增加x. 3, 查询子树权值和. 先不考虑换根, 考虑子树x加v的贡献 (1)对fa[x]到根的树链贡献 ...

  8. Jamie and Tree (dfs序 + 最近公共祖先LCA)

    题面 题解 我们求它子树的权值和,一般用dfs序把树拍到线段树上做. 当它换根时,我们就直接把root赋值就行了,树的结构不去动它. 对于第二个操作,我们得到的链和根的相对位置有三种情况: 设两点为A ...

  9. CF916E

    Codeforces 916E 简要题解Description Description 有一棵n个点的树,每个节点上有一个权值wi,最开始根为1号点.现在有3种类型的操作: 1 root, 表示将根设 ...

随机推荐

  1. 读书笔记 之java编程思想

    本阶段我正在读java的编程思想这本书,这本书只是刚读了第一章的一部分,有些有些要记得所以记录下来, 我认为要记得有就是复用这样可以对对象进行增强,将一个类作为下一个类中基本类型,这样达到的服用的目的 ...

  2. 饭来了小组Alpha冲刺阶段记录

    一.第一天 日期:2018/6/13 1.1今日完成任务情况以及遇到的问题 侯晓东: 1.完成任务项:具体了解了微信小程序的开发流程,然后大致规划了我们项目的进度和完成节点:汇总组员的进度,写博客:画 ...

  3. Enterprise Library 4.1 参考源码索引

    http://www.projky.com/entlib/4.1/Microsoft/Practices/EnterpriseLibrary/AppSettings/Configuration/Des ...

  4. 用windbg检查.NET线程池设置

    比如我们在machine.config中进行了这样的设置(8核CPU): <processModel maxWorkerThreads="100" maxIoThreads= ...

  5. beta——5

    一.提供当天站立式会议照片一张: 二. 每个人的工作 (有work item 的ID) (1) 昨天已完成的工作: 对用户功能的添加. (2) 今天计划完成的工作: web发布 (3) 工作中遇到的困 ...

  6. 10.13课堂Scrum站立会议

    项目名称:C#实现的连连看游戏 小组名称:计信F4 开会时间 :2016年10月11日 20:20~20:40 组长:张政 成员:张金生,武志远,李泉 内容: 昨日已完成: 张政:构建基础逻辑,实现游 ...

  7. jsp页面has already been called for this response错误解决方法。

    创建验证码的jsp页面提示错误:has already been called for this response <%@ page contentType="image/jpeg&q ...

  8. PHP ini_set

    PHP ini_set用来设置php.ini的值,在函数执行的时候生效,对于虚拟空间来说,很方便,下面为大家介绍下此方法的使用   PHP ini_set用来设置php.ini的值,在函数执行的时候生 ...

  9. 2013长沙网赛E题Travel by Bike

    题目链接:http://acm.zju.edu.cn/changsha/showProblem.do?problemId=26 题意:一个人从一个地方到另一个地方,长度为L,每小时速度为speed,周 ...

  10. SQL有三个类型的索引,唯一索引 不能有重复,但聚集索引,非聚集索引可以有重复

    重要: (1) SQL如果创建时候,不指定类型那么默认是非聚集索引 (2) 聚集索引和非聚集索引都可以有重复记录,唯一索引不能有重复记录. (3) 主键 默认是加了唯一约束的聚集索引,但是也可以在主键 ...