DFS序--一般都要转化为顶点到每个点

There is a rooted tree with n nodes, number from 1-n. Root’s number is 1.Each node has a value ai.

Initially all the node’s value is 0.

We have q operations. There are two kinds of operations.

1 v x k : a[v]+=x , a[v’]+=x-k (v’ is child of v) , a[v’’]+=x-2*k (v’’ is child of v’) and so on.

2 v : Output a[v] mod 1000000007(10^9 + 7).

Input

First line contains an integer T (1 ≤ T ≤ 3), represents there are T test cases.

In each test case:

The first line contains a number n.

The second line contains n-1 number, p2,p3,…,pn . pi is the father of i.

The third line contains a number q.

Next q lines, each line contains an operation. (“1 v x k” or “2 v”)

1?≤?n?≤?3*10^5

1?≤?pi?<?i

1?≤?q?≤?3*10^5

1?≤?v?≤?n; 0?≤?x?<?10^9?+?7; 0?≤?k?<?10^9?+?7

Output

For each operation 2, outputs the answer.

Sample Input

1 3 1 1 3 1 1 2 1 2 1 2 2

Sample Output

2 1

更新v的时候,他的子孙的值就是这个 x+d[v]*k-d[s]*k

一个树状数组维护x+d[v]*k
一个维护sigma(k)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=3e5+88;
const LL mod=1e9+7;
int l[N],r[N];
LL dep[N],A[N],B[N];
int tot,head[N],n,t;
struct node{
   int next,to;
}e[N];
void add(int u,int v){
   e[tot].next=head[u];
   e[tot].to=v;
   head[u]=tot++;
}
void addA(int pos,LL val){
    for(;pos<=n+8;pos+=pos&(-pos))
        A[pos]=(A[pos]+val)%mod;
}
void addB(int pos,LL val){
    for(;pos<=n+8;pos+=pos&(-pos))
        B[pos]=(B[pos]+val)%mod;
}
void dfs(int x){
   l[x]=t++;
   for(int i=head[x];i+1;i=e[i].next){
    dep[e[i].to]=dep[x]+1;dfs(e[i].to);
   }
   r[x]=t;
}
LL sum(int pos,int pp){
    LL ret=0,ans=0;
    for(int i=pos;i;i-=i&(-i))
        ret=(ret+A[i])%mod;
    for(int i=pos;i;i-=i&(-i))
        ans=(ans+B[i])%mod;
    ans=((LL)ans*dep[pp])%mod;
    return (ret-ans+2*mod)%mod;
}
int main(){
     int m,T,x,k,v,op;
     for(scanf("%d",&T);T--;){
        scanf("%d",&n);
        for(int i=0;i<=n;++i) head[i]=-1;
        for(int i=0;i<=n+1;++i) A[i]=B[i]=0;
        tot=0,dep[1]=t=1;
        for(int i=2;i<=n;++i)
        {
            scanf("%d",&x);add(x,i);
        }
        dfs(1);
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&op,&v);
            if(op==1){
                scanf("%d%d",&x,&k);
                LL ct=(x+dep[v]*k)%mod;
                addA(l[v],ct);
                addA(r[v],-ct);
                addB(l[v],k);
                addB(r[v],-k);
            }
            else printf("%I64d\n",sum(l[v],v));
        }
     }
}

PSHTTR: Pishty 和城堡
题目描述 Pishty 是生活在胡斯特市的一个小男孩。胡斯特是胡克兰境内的一个古城,以其中世纪风格 的古堡和非常聪明的熊闻名全国。 胡斯特的镇城之宝是就是这么一座古堡,历史上胡斯特依靠这座古堡抵挡住了疯人国的大军。 对于 Pishty 来说,真正吸引他的是古堡悠长的走廊和高耸的钟楼,以及深藏于其中的秘密…… 古堡可以用一棵 N 个节点的树的描述,树中有 N ?1 条无向边,每条边有一个魔法数字 C。 当一个旅游团参观古堡时,他们会选择树上 U 到 V 的路径游览。他们认为,如果一条边的魔 法数字不超过 K,那么这条边是有趣的。而一条路径的吸引力就是路径上所有有趣的边的魔法数 字的异或和。 胡克兰的国王希望大力发展旅游业,因此他下令求出所有旅游团的游览路径的吸引力。而 Pishty立志成为国王身边的骑士,便自告奋勇承担了这一任务。但旅游团实在太多了,他也算不过 来。所以,请你帮Pishty解决这一问题:给定 M 个旅游团的旅游路径,请你求出路径的吸引力。
输入格式
输入的第一行包含一个整数 T,代表测试数据的组数。接下来是 T 组数据。 每组数据的第一行包含一个整数 N,代表树的节点个数。 接下来 N ?1 行,每行描述一条边。每行包含三个整数 U,V,C,代表节点 U 和 V 之间连有 一条魔法数字为 C 的边。 接下来一行包含一个整数 M,代表旅游团的数量。 接下来 M 行,每行包含三个整数 U,V,K,描述一个旅游团。
输出格式
对于每个旅游团,输出一行,包含一个整数,代表其路径的吸引力。
数据范围和子任务 ? 1 ≤ T ≤ 5 ? 1 ≤ N,M ≤ 105
? 1 ≤ U,V ≤ N ? 1 ≤ C,K ≤ 109
子任务 1(10 分): ? 1 ≤ N,M ≤ 10
子任务 2(20 分): ? 1 ≤ N,M ≤ 103
子任务 3(70 分): ? 无附加限制
样例数据
输入
1
5
1 2 1
2 3 2
2 4 5
3 5 10
6
5 4 5
5 4 10
5 4 1
1 2 1
4 1 10
1 5 8
输出
7
13
0
1
4
3

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+88;
int L[N],R[N],dep[N],tot,t,head[N],fa[N],ans[N];
struct et{
   int v,next;
}e1[N<<2];
struct node{
   int u,v,w,id;
   bool operator <(const node &A)const{
   return w<A.w;
   }
}sor[N],e2[N];
struct tct{
   int l,r,w;
}tr[N<<2];
void add(int u,int v){
   e1[tot].v=v;e1[tot].next=head[u];head[u]=tot++;
   e1[tot].v=u;e1[tot].next=head[v];head[v]=tot++;
}
void push_down(int rt){
    if(tr[rt].l==tr[rt].r) return;
   if(tr[rt].w){
    tr[rt<<1].w^=tr[rt].w;tr[rt<<1|1].w^=tr[rt].w;
    tr[rt].w=0;
   }
}
void build(int l,int r,int rt){
   tr[rt].l=l,tr[rt].r=r,tr[rt].w=0;
   if(l==r) return;
   int mid=(l+r)>>1;
   build(l,mid,rt<<1);
   build(mid+1,r,rt<<1|1);
}
void dfs(int u){
    L[u]=t++;
   for(int i=head[u];i+1;i=e1[i].next){
    int v=e1[i].v;
    if(v==fa[u]) continue;
    dep[v]=dep[u]+1;
    fa[v]=u;
    dfs(v);
   }
   R[u]=t;
}
void add(int L,int R,int rt,int val){
   int l=tr[rt].l,r=tr[rt].r;
   if(l==L&&R==r) {
    tr[rt].w^=val;
    return;
   }
   push_down(rt);
   int mid=(l+r)>>1;
   if(R<=mid) add(L,R,rt<<1,val);
   else if(L>mid) add(L,R,rt<<1|1,val);
   else {
    add(L,mid,rt<<1,val);
    add(mid+1,R,rt<<1|1,val);
   }
}
int query(int pc,int rt){
    int l=tr[rt].l,r=tr[rt].r;
    if(l==r) return tr[rt].w;
    int mid=(l+r)>>1;
    push_down(rt);
    if(pc<=mid) return query(pc,rt<<1);
    else return query(pc,rt<<1|1);
}
int main(){
    int T,n,m,u,v,w;
    for(scanf("%d",&T);T--;){
        scanf("%d",&n);
        build(1,n,1);
        memset(head,-1,sizeof(head));
        tot=0;
        t=1;
        for(int i=1;i<n;++i) {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v);
            sor[i].u=u,sor[i].v=v,sor[i].w=w;
        }
        dfs(1);
        scanf("%d",&m);
        for(int i=1;i<=m;++i){
            scanf("%d%d%d",&e2[i].u,&e2[i].v,&e2[i].w);
            e2[i].id=i;
        }
        sort(sor+1,sor+n);
        sort(e2+1,e2+m+1);
        int ii=1;
        for(int i=1;i<=m;++i){
            while(sor[ii].w<=e2[i].w&&ii<n) {
                int u=sor[ii].u,v=sor[ii].v;
                if(dep[u]<dep[v]) swap(u,v);
                add(L[u],R[u]-1,1,sor[ii].w);
                ++ii;
            }
            int t1=query(L[e2[i].u],1),t2=query(L[e2[i].v],1);
            ans[e2[i].id]=t1^t2;
        }
        for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
    }
}


时间: 2024-10-20 06:27:48

DFS序--一般都要转化为顶点到每个点的相关文章

CF1328E-Tree Queries(补) (dfs序)

题目链接: https://codeforces.com/contest/1328/problem/E 思路: 题目大意就是问你从顶点到另一个点u,是否存在一条链,满足询问中的每个点都在 链上或者点的父节点在链上,首先我们可以发现深度最深的那个点max肯定是在链中的, 那么接下来我们只需要将每个点和他的父节点跟这个max点进行比较即可.如果该点跟max点 在一条链上,那么max点肯定在该点的子树中,那么问题转化为如何判断max点是否在该点的 子树中,我们可以用dfs序来判断. 代码: #incl

HDU 5156 - Harry and Christmas tree (dfs序+离线树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=5156 BC#25的C题. 题意是:给出一颗大小为n的树,以1为根,然后给出m次染色,每次将节点u加上一种颜色(一个节点可以有多个颜色). 最后查询树上每个节点对应子树上包含的不同颜色数量. 当时这场比赛没有做,回来看一下题目,没看标解就试着敲了一遍,于是解题思路从一开始就走上了不归路. 标解是O(n+m)的方法,主要思路是将问题转为:一次染色表示将u到根节点的路径都染上这种颜色. 但这样做需要去重,因为如果u

【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒.实验在一个封闭的局域网内进行.局域网内有n台计算机,编号为1~n.一些计算机之间通过网线直接相连,形

[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

[NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失.l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失.例如,阿狸输入aPaPBbP

DFS序详解

dfs序就是一棵树在dfs遍历时组成的节点序列. 它有这样一个特点:一棵子树的dfs序是一个区间. 下面是dfs序的基本代码: void dfs(int x,int pre,int d){//L,R表示一个子树的范围 L[x]=++tot; dep[x]=d; for(int i=0;i<e[x].size();i++){ int y=e[x][i]; if(y==pre)continue; dfs(y,x,d+1); } R[x]=tot; } 给定一颗树, 和每个节点的权值.下面有7个经典的

HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形dp, 对于每条链u,v,w,我们只在lca(u,v)的顶点上处理它 让dp[i]表示以i为根的指数的最大值,sum[i]表示dp[vi]的和(vi为i的儿子们) 则i点有两种决策,一种是不选以i为lca的链,则dp[i]=sum[i]. 另一种是选一条以i为lca的链,那么有转移方程:dp[i]=

线段树+dfs序(Apple Tree )(Assign the task )

线段树+dfs序 给定一棵n个节点的树,m次查询,每次查询需要求出某个节点深度为h的所有子节点. 作为预处理,首先将树的所有节点按深度保存起来,每个深度的所有节点用一个线性结构保存,每个深度的节点相对顺序要和前序遍历一致. 然后从树的根节点进行dfs,对于每个节点记录两个信息,一个是dfs进入该节点的时间戳in[id],另一个是dfs离开该节点的时间戳out[id]. 最后对于每次查询,求节点v在深度h的所有子节点,只需将深度为h并且dfs进入时间戳在in[v]和out[v]之间的所有节点都求出

K. Random Numbers(Gym 101466K + 线段树 + dfs序 + 快速幂 + 唯一分解)

题目链接:http://codeforces.com/gym/101466/problem/K 题目: 题意: 给你一棵有n个节点的树,根节点始终为0,有两种操作: 1.RAND:查询以u为根节点的子树上的所有节点的权值的乘积x,及x的因数个数. 2.SEED:将节点u的权值乘以x. 思路: 比赛时少看了因数不大于13这句话,然后本题难度增加数倍,肝了两个小时都没肝出来,对不起队友啊,今天的组队训练赛实力背锅…… 这题一眼线段树,由于是对一棵子树进行处理,因此我们采用常规套路,借助dfs序将子树

POJ3321 - Apple Tree DFS序 + 线段树或树状数组

Apple Tree:http://poj.org/problem?id=3321 题意: 告诉你一棵树,每棵树开始每个点上都有一个苹果,有两种操作,一种是计算以x为根的树上有几个苹果,一种是转换x这个点上的苹果,就是有就去掉,没有就加上. 思路: 先对树求一遍dfs序,每个点保存一个l,r.l是最早到这个点的时间戳,r是这个点子树中的最大时间戳,这样就转化为区间问题,可以用树状数组,或线段树维护区间的和. #include <algorithm> #include <iterator&