题面

传送门

题目大意:

给定一棵树,每个点都有权值,边的长度均为1,有两种操作

操作1:将节点u的值增加x,并且对于u的子树中的任意一个点v,将它的值增加x-dist(u,v)*k, dist(u,v)表示u,v之间的距离

操作2:查询节点u的值

分析

这类题目需要用到一个重要的思想:将树上操作转化为区间操作

通过DFS序我们可以实现这一点.

对于每个节点x,我们记录它在前序遍历中的位置l[x],再一次回到x时的序号r[x],则x及其子树的区间为前序遍历中的[l[x],r[x]]

如:



这棵树的前序遍历为0 1 4 5 2 6 3 7 8 9 10

后序遍历为4 5 1 6 2 7 8 9 10 3 0

对于点3来说,它在前序遍历中的序号为7,遍历完7,8,9后回到3的序号为10,则区间为[7,10]

将树上操作转化为区间之后,我们处理两种操作

显然是用线段树的区间修改和单点查询实现

每次修改时,对于u的后代v,我们发现它增加的值=x+k(d[v]−d[u])=x+k×d[u]−k×d[v]" role="presentation" style="position: relative;">=x+k(d[v]−d[u])=x+k×d[u]−k×d[v]=x+k(d[v]−d[u])=x+k×d[u]−k×d[v] (d[x]表示x的深度)

其中,对于u的每个后代v,x+k×d[u]" role="presentation" style="position: relative;">x+k×d[u]x+k×d[u]都是一样的,可以批量修改,而k×d[v]" role="presentation" style="position: relative;">k×d[v]k×d[v]则由每个节点决定

因此,我们用两棵线段树维护

一棵维护x+k×d[u]" role="presentation" style="position: relative;">x+k×d[u]x+k×d[u],一棵维护k" role="presentation" style="position: relative;">kk

修改时,我们把x+k×d[u]" role="presentation" style="position: relative;">x+k×d[u]x+k×d[u]和k" role="presentation" style="position: relative;">kk分别累加到区间[l[u],r[u]]中每一个点

查询时,我们可以求出每个节点的x+k×d[u]" role="presentation" style="position: relative;">x+k×d[u]x+k×d[u]的总和a,以及k" role="presentation" style="position: relative;">kk的总和b

答案就是a−b∗d[u]" role="presentation" style="position: relative;">a−b∗d[u]a−b∗d[u]

时间复杂度O(n+qlog2n)" role="presentation" style="position: relative;">O(n+qlog2n)O(n+qlog2n)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 300005
#define mod 1000000007ll
using namespace std;
int n,q;
struct edge{
int from;
int to;
int next;
}E[maxn<<1];
int size;
int head[maxn];
void add_edge(int u,int v){
size++;
E[size].from=u;
E[size].to=v;
E[size].next=head[u];
head[u]=size;
} struct segment_tree{
struct node{
int l;
int r;
long long mark;
long long v;
}tree[maxn<<2];
segment_tree(){
memset(tree,0,sizeof(tree));
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
tree[pos].mark=0;
tree[pos].v=0;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
if(tree[pos].mark){
tree[pos<<1].mark=(tree[pos].mark+tree[pos<<1].mark)%mod;
tree[pos<<1|1].mark=(tree[pos].mark+tree[pos<<1|1].mark)%mod;
tree[pos<<1].v=(tree[pos].mark+tree[pos<<1].v)%mod;
tree[pos<<1|1].v=(tree[pos].mark+tree[pos<<1|1].v)%mod;
tree[pos].mark=0;
}
}
void update(int L,int R,long long v,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
tree[pos].v=(tree[pos].v+v)%mod;
tree[pos].mark=(tree[pos].mark+v)%mod;
return ;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
if(L<=mid) update(L,R,v,pos<<1);
if(R>mid) update(L,R,v,pos<<1|1);
return;
}
long long query(int L,int R,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
return tree[pos].v;
}
push_down(pos);
int mid=(tree[pos].l+tree[pos].r)>>1;
long long ans=0;
if(L<=mid) ans=(ans+query(L,R,pos<<1))%mod;
if(R>mid) ans=(ans+query(L,R,pos<<1|1))%mod;
return ans;
}
};
segment_tree T1,T2;
int l[maxn],r[maxn];
int deep[maxn];
int cnt=0;
void dfs(int x,int fa){
l[x]=++cnt;
deep[x]=deep[fa]+1;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs(y,x);
}
}
r[x]=cnt;
}
int main(){
int p,cmd;
int v,x,k;
scanf("%d",&n);
int root;
for(int i=2;i<=n;i++){
scanf("%d",&p);
add_edge(i,p);
add_edge(p,i);
}
dfs(1,0);
T1.build(1,n,1);
T2.build(1,n,1);
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d",&cmd);
if(cmd==1){
scanf("%d %d %d",&v,&x,&k);
T1.update(l[v],r[v],(long long)x+(long long)deep[v]*k%mod,1);
T2.update(l[v],r[v],(long long)k,1);
}else{
scanf("%d",&v);
long long ans=(T1.query(l[v],l[v],1)-(long long)deep[v]*T2.query(l[v],l[v],1)+mod)%mod;
if(ans<0) ans+=mod;
printf("%I64d\n",ans%mod);
}
}
}

Codeforces 396C (DFS序+线段树)的更多相关文章

  1. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  2. Codeforces 1110F(DFS序+线段树)

    题面 传送门 分析 next_id = 1 id = array of length n filled with -1 visited = array of length n filled with ...

  3. Codeforces 1132G(dfs序+线段树)

    题面 传送门 分析 对于每一个数a[i],找到它后面第一个大于它的数a[p],由p向i连边,最终我们就会得到一个森林,且p是i的父亲.为了方便操作,我们再增加一个虚拟节点n+1,把森林变成树. 由于序 ...

  4. Educational Codeforces Round 6 E dfs序+线段树

    题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...

  5. Codeforces 343D Water Tree(DFS序 + 线段树)

    题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...

  6. Codeforces Round #442 (Div. 2)A,B,C,D,E(STL,dp,贪心,bfs,dfs序+线段树)

    A. Alex and broken contest time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  7. CodeForces 877E Danil and a Part-time Job(dfs序+线段树)

    Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...

  8. 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

    3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] D ...

  9. BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)

    题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...

随机推荐

  1. Bean的生命周期与JVM**

    案例: 在service里定义了一个全局变量,这类变量只能是final的.如果不是,在代码中一旦有地方给该变量进行业务赋值.当不满足赋值条件时变量仍然保留上次的赋值的值. 这是因为Bean没有销毁. ...

  2. npm cache clean --force

    当出现这个问题时npm ERR! Unexpected end of JSON input while parsing near '...,"dist":{"shasum ...

  3. 死锁(deadlocks)

    1.定义 所谓死锁<DeadLock>: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象.若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了 ...

  4. codeforces 868C - Qualification Rounds(构造)

    原题链接:http://codeforces.com/problemset/problem/868/C 题意:有k个队伍参加比赛,比赛有n个预选的题目,有些队伍对已经事先知道了一些题目.问能不能选出若 ...

  5. [USACO08FEB]连线游戏Game of Lines

    题目背景 Farmer John最近发明了一个游戏,来考验自命不凡的贝茜. 题目描述 Farmer John has challenged Bessie to the following game: ...

  6. 6.12友谊赛T4城市交通费题解

    与普通的最短路径不同的是,题目中新引入了一个计入总体的费用——城市建设费.由于城市建设费由整体的某最大值决定,导致解没有最优子结构的性质,给思维带来难度. 既然最棘手的是城市建设费,我们就对它分类讨论 ...

  7. 170826-关于spring的知识点及练习

    1.Spring作用: 1.生态体系庞大,全能型选手![springmvc是其一个子模块,jdbcTemplate能直接操作数据库!] 2.将其他组件粘合在一起 3.IOC容器和AOP[Aspect ...

  8. 普通用户sudo权限

    需求: 1>创建一个saipu普通用户,不允许使用 rm 和 passwd root 和 sudo su - root 命令,其他命令均允许且 sudo 时不用输入密码 2>创建一个lwd ...

  9. 构建嵌入式Linux交叉编译工具链

    开源交叉编译工具链制作方法汇总: 1) 使用crosstool/crosstool-ng生成 2) 使用buildroot生成 ARM交叉编译工具链说明: 1) arm-linux-gcc是一个集合命 ...

  10. SeaJS基本开发原则

    SeaJS基本开发原则在讨论SeaJS的具体使用前,先介绍一下SeaJS的模块化理念和开发原则.使用SeaJS开发JavaScript的基本原则就是:一切皆为模块.引入SeaJS后,编写JavaScr ...