Codeforces 396C (DFS序+线段树)
题面
传送门
题目大意:
给定一棵树,每个点都有权值,边的长度均为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序+线段树)的更多相关文章
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- Codeforces 1110F(DFS序+线段树)
题面 传送门 分析 next_id = 1 id = array of length n filled with -1 visited = array of length n filled with ...
- Codeforces 1132G(dfs序+线段树)
题面 传送门 分析 对于每一个数a[i],找到它后面第一个大于它的数a[p],由p向i连边,最终我们就会得到一个森林,且p是i的父亲.为了方便操作,我们再增加一个虚拟节点n+1,把森林变成树. 由于序 ...
- Educational Codeforces Round 6 E dfs序+线段树
题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...
- Codeforces 343D Water Tree(DFS序 + 线段树)
题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...
- 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 ...
- 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 ...
- 【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 339 Solved: 130[Submit][Status][Discuss] D ...
- BZOJ2434 [Noi2011]阿狸的打字机(AC自动机 + fail树 + DFS序 + 线段树)
题目这么说的: 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: 输入小 ...
随机推荐
- js中的 与和或 , && ,||
|| 1.只要"||"前面为false,不管"||"后面是true还是false,都返回"||"后面的值. 2.只要"||&quo ...
- Task4.用PyTorch实现多层网络
1.引入模块,读取数据 2.构建计算图(构建网络模型) 3.损失函数与优化器 4.开始训练模型 5.对训练的模型预测结果进行评估 import torch.nn.functional as F im ...
- scoket、TCP、UDP、WebService选型
抱着去转型产品经理的方向去面试了一家公司,面试完很惭愧,不过见到了人事我也很意外,因为其实表现也没那么好,不过在此谈谈我的感受. 1.有3轮,前2轮都是先让我做自我介绍(我都说的很干脆,直接哪年毕业, ...
- Day01_初识Python
Python简介 Python的历史 1.1989年圣诞节:Guidao von Rossum开始写Python语言的编译器 2.1991年2月:第一个Python编译器(同时也是解释器)诞生,他是使 ...
- spring 知识结构
- 关联规则挖掘--Eclat算法
- 设计模式学习笔记——Adapter 适配器模式
适配器设计模式的适应场景: 一般情况是上端固定,下端固定,下端功能不满足或跟上端不协调,使用适配器重新包一层(继承适配器接口,以满足上端需求,继承下层类,以调用方法),使下端代码能满足上端需求(欺骗, ...
- J - Mr.Panda and TubeMaster
题解 我们可以把每个格子拆成两个点,一个表示横向的,一个表示纵向的,相邻的格子横向和纵向连边. 如果直接按照题意做的话,我们应当在横向和纵向的点之间连边,有限制的边设下界为1,然后跑可行流. 或者考虑 ...
- StringUtils.join()
org.apache.commons.lang.StringUtils; StringUtils.join(null) = null StringUtils.join([]) ...
- Oracle---智斗ORA01427
下面是我在做更新的时候遇到报ORA-01427,单行子查询返回多行值,原因是红色部分返回了多行值 UPDATE IN_MO IM SET IM.BOM_ID = (S ...