CodeForce 916E Jamie and Tree(线段树+分类讨论)

标签: codeforces 线段树 分类讨论
8人阅读 评论(0) 收藏 举报
分类:
E. Jamie and Tree
time limit per test:2.5 seconds
memory limit per test:256 megabytes
input:standard input
output:standard output

To your surprise, Jamie is the final boss! Ehehehe.

Jamie has given you a tree with n vertices, numbered from 1 to n. Initially, the root of the tree is the vertex with number 1. Also, each vertex has a value on it.

Jamie also gives you three types of queries on the tree:

1 v — Change the tree's root to vertex with number v.

2 u v x — For each vertex in the subtree of smallest size that contains u and v, add x to its value.

3 v — Find sum of values of vertices in the subtree of vertex with number v.

A subtree of vertex v is a set of vertices such that v lies on shortest path from this vertex to root of the tree. Pay attention that subtree of a vertex can change after changing the tree's root.

Show your strength in programming to Jamie by performing the queries accurately!

Input

The first line of input contains two space-separated integers n and q (1 ≤ n ≤ 105, 1 ≤ q ≤ 105) — the number of vertices in the tree and the number of queries to process respectively.

The second line contains n space-separated integers a1, a2, ..., an ( - 108 ≤ ai ≤ 108) — initial values of the vertices.

Next n - 1 lines contains two space-separated integers ui, vi (1 ≤ ui, vi ≤ n) describing edge between vertices ui and vi in the tree.

The following q lines describe the queries.

Each query has one of following formats depending on its type:

1 v (1 ≤ v ≤ n) for queries of the first type.

2 u v x (1 ≤ u, v ≤ n,  - 108 ≤ x ≤ 108) for queries of the second type.

3 v (1 ≤ v ≤ n) for queries of the third type.

All numbers in queries' descriptions are integers.

The queries must be carried out in the given order. It is guaranteed that the tree is valid.

Output

For each query of the third type, output the required answer. It is guaranteed that at least one query of the third type is given by Jamie.

Examples
Input
Copy
6 7
1 4 2 8 5 7
1 2
3 1
4 3
4 5
3 6
3 1
2 4 6 3
3 4
1 6
2 2 4 -5
1 4
3 3
Output
Copy
27
19
5
Input
Copy
4 6
4 3 5 6
1 2
2 3
3 4
3 1
1 3
2 2 4 3
1 1
2 2 4 -3
3 1
Output
Copy
18
21
Note

The following picture shows how the tree varies after the queries in the first sample.
                    

      大致题意:给你一棵树,每个节点上面有一个权值。然后有三种询问:1、换根;2、把点x、y在现有树情况下的LCA的子树的所有点权增加一个值;3、求某个节点在现有树情况下的点权和。
      很显然的一个线段树经典题目,首先求所有点的dfs序,然后根据dfs序建立线段树,可以动态维护子树的权值和。但是这题的关键在于换根。因为换根之后树的形态发生了改变,对应的子树情况也会发生改变,不能简单的用初始情况下的dfs序。但是我们显然也不能真的去换根。


         我们现在重新考虑一下换根之后,对于2和3操作如何处理。对于操作3,根据询问点的不同,我们可以分为两种情况。
                                                    
        在这个树中,root表示初始情况下的根,p表示换根之后新的根,a表示询问的点,显然这种情况下,换根对a不产生影响。直接按照初始的状态下输出子树权值和即可。

                                                    
        同理,在这种情况下,我们发现p变为根之后对a的子树产生了影响。而这种影响也很容易计算,考虑首先求整棵树所有的点权和,然后再减去不是所求的一部分。这个部分通过图中我们可以看到,就是b的子树的点权和。通俗点说,就是a与p路径中,最靠近a的一个节点。


        然后再考虑操作2。操作2由于还要求两个点在现有树情况下的LCA,所有情况可能会复杂一点。但是总的来说还是可以总结的。首先就是这个LCA。

                                                   
           在原树中,x、y的LCA是a但是在换根之后y是LCA。经过观察可以发现,在所有情况下,LCA都是lca(x,y)、lca(x,p)和lca(y,p)四个点中深度最大的点。
           当我们确定了LCA之后,剩下的修改操作就与操作2类似了。


       然后说说一些细节的地方。首先,每个点的子树的编号的点的范围根据dfs遍历时的dfs序确定,每个点对应记录一个st[]和ed[],表示以该点为根的子树中第一个和最后一个点的编号。其次,对于操作3中,减去的子树的确定,我们可以利用之前LCA求的dp倍增数组在logN时间内一步步确定。最后是在修改和查询的时候特殊判断对根的操作。具体见代码:
#include<bits/stdc++.h>
#define LL long long
#define N 100010
using namespace std;

int st[N],ed[N],Rank[N];
int n,q,cnt,root=1;

struct Segment_Tree
{
	LL a[N];
	struct node{LL lazy,sum;int l,r;} tree[N<<2];
	inline void push_up(int i){tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;}

	inline void build(int i,int l,int r)
	{
		tree[i]=node{0,0,l,r};
		if (l==r){tree[i].sum=a[Rank[l]];return;}
		int mid=(l+r)>>1;
		build(i<<1,l,mid);
		build(i<<1|1,mid+1,r);
		push_up(i);
	}

	inline void push_down(int i)
	{
		tree[i<<1].lazy+=tree[i].lazy;
		tree[i<<1|1].lazy+=tree[i].lazy;
		tree[i<<1].sum+=(tree[i<<1].r-tree[i<<1].l+1)*tree[i].lazy;
		tree[i<<1|1].sum+=(tree[i<<1|1].r-tree[i<<1|1].l+1)*tree[i].lazy;
		tree[i].lazy=0;
	}

	inline void update(int i,int l,int r,LL x)
	{
		if ((tree[i].l==l)&&(tree[i].r==r))
		{
			tree[i].sum+=(r-l+1)*x;
			tree[i].lazy+=x;
			return;
		}
		if (tree[i].lazy!=0) push_down(i);
		int mid=(tree[i].l+tree[i].r)>>1;
		if (mid>=r) update(i<<1,l,r,x);
		else if (mid<l) update(i<<1|1,l,r,x);
		else
		{
			update(i<<1,l,mid,x);
			update(i<<1|1,mid+1,r,x);
		}
		push_up(i);
	}

	inline LL getsum(int i,int l,int r)
	{
		if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].sum;
		if (tree[i].lazy!=0) push_down(i);
		int mid=(tree[i].l+tree[i].r)>>1;
		if (mid>=r) return getsum(i<<1,l,r);
		else if (mid<l) return getsum(i<<1|1,l,r);
		else return getsum(i<<1,l,mid)+getsum(i<<1|1,mid+1,r);
	}
} seg;

namespace LCA
{
    int dp[N][21],dep[N];
    vector<int> g[N];

    inline void dfs(int x,int fa)
    {
        for(int i=0;i<g[x].size();i++)
        {
            int y=g[x][i];
            if (y==fa) continue;
            dep[y]=dep[x]+1;
            dp[y][0]=x; dfs(y,x);
        }
    }

    void ST()
    {
        for(int j=0;j<19;j++)
            for(int i=1;i<=n;i++)
                dp[i][j+1]=dp[dp[i][j]][j];
    }

    inline int lca(int u,int v)
    {
        if (u==v) return u;
        if (dep[v]>dep[u]) swap(u,v);
        for(int i=18;i>=0;i--)
            if (dep[dp[u][i]]>=dep[v]) u=dp[u][i];
        if (u==v) return u;
        for(int i=18;i>=0;i--)
            if (dp[u][i]!=dp[v][i]) u=dp[u][i],v=dp[v][i];
        return dp[u][0];
    }
}

int getpos(int x,int y)
{
    using namespace LCA;
    for(int i=18;i>=0;i--)
    {
        if (dep[dp[y][i]]<=dep[x]) continue;
        y=dp[y][i];
    }
    return y;
}

void label(int x,int fa)
{
    st[x]=++cnt;
    Rank[cnt]=x;
    using namespace LCA;
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        if (fa==y) continue;
        label(y,x);
    }
    ed[x]=cnt;
}

int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
        scanf("%lld",&seg.a[i]);
    using namespace LCA; dep[0]=-1;
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    label(1,0);
    dfs(1,0); ST();
    seg.build(1,1,n);
    while(q--)
    {
        int op,x,y,val;
        scanf("%d%d",&op,&x);
        if (op==1) root=x;
        if (op==2)
        {
            scanf("%d%d",&y,&val);
            int a=lca(x,y);
            int b=lca(x,root);
            int c=lca(y,root);
            if (dep[a]>dep[b]) x=a; else x=b;
            if (dep[c]>dep[x]) x=c; y=lca(x,root);
            if (y==x&&x!=root)
            {
                seg.update(1,1,n,val);
                int pos=getpos(x,root);
                seg.update(1,st[pos],ed[pos],-val);
            } else
            if (x!=root) seg.update(1,st[x],ed[x],val);
                    else seg.update(1,1,n,val);
        }
        if (op==3)
        {
            int LCA=lca(x,root);
            if (LCA==x&&x!=root)
            {
                LL a=seg.getsum(1,1,n);
                int pos=getpos(x,root);
                LL b=seg.getsum(1,st[pos],ed[pos]);
                printf("%lld\n",a-b);
            } else
            if (x!=root) printf("%lld\n",seg.getsum(1,st[x],ed[x]));
                    else printf("%lld\n",seg.getsum(1,1,n));

        }
    }
    return 0;
}

查看评论

CodeForces 343D Water Tree dfs序 + 线段树

题目:http://codeforces.com/problemset/problem/343/D 题意:给定一个树,树上有n个点,每个点是一个蓄水池,初始全为空。首先输入一个n,然后输入n - ...
  • discreeter
  • discreeter
  • 2016-05-24 12:14:58
  • 962

Jamie and Tree codeforces 916E

题目大意:给出一棵树以及每个结点的权值,初始时根为1。有3种操作:一是将根换为x;二是给出两个节点u,v,把包含这两个点的最小子树中每个节点权值加上x;三是查询以u为根的权值和。题解:不考虑换根时,需...
  • nudt_spy
  • nudt_spy
  • 2018-04-06 02:30:54
  • 10

codeforces 916 A Jamie and Alarm Snooze

一道很简单的模拟题。
  • finalcsdn
  • finalcsdn
  • 2018-01-20 00:52:46
  • 325

【CodeForces】343D Water tree (线段树好题!还未弄懂)

/*此题的方法除了用线段树求子树,通过标记父亲,更新儿子的方法,来更新祖先,学习了。 对于建树的方法由于并没有说明父亲与儿子的顺序,所以需要通过两次添加。 并且pre变量可以获得父亲的位置,还未弄懂!...
  • u013611908
  • u013611908
  • 2015-02-26 22:24:18
  • 777

Codeforces 916 C. Jamie and Interesting Graph (构造)

Description Jamie has recently found undirected weighted graphs with the following properties ve...
  • qq_28954601
  • qq_28954601
  • 2018-01-20 11:33:38
  • 152

Codeforces Round #457 (Div. 2)——B. Jamie and Binary Sequence(贪心)

比赛刚a了这题就说标程错了ORZ,我还是太菜惹(⋟﹏⋞),现在应该是改好了吧???如有错误欢迎指正 B. Jamie and Binary Sequence (changed after ro...
  • j2_o2
  • j2_o2
  • 2018-01-20 09:43:26
  • 210

Codeforces Round #457(Div.2)Problem E Jamie and Tree(DFS序+倍增算法+LCA+树状数组)

E. Jamie and Tree time limit per test 2.5 seconds memory limit per test 256 megabytes ...
  • Shili_Xu
  • Shili_Xu
  • 2018-02-07 20:10:35
  • 62

Codeforces Round #457(Div.2)Problem D Jamie and To-do List(可持久化Trie树)

D. Jamie and To-do List time limit per test 2 seconds memory limit per test 512 megabyte...
  • Shili_Xu
  • Shili_Xu
  • 2018-02-07 19:32:02
  • 117

Codeforces-916B:Jamie and Binary Sequence (changed after round)(思维)

B. Jamie and Binary Sequence (changed after round) time limit per test 2 seconds memory l...
  • Mitsuha_
  • Mitsuha_
  • 2018-01-20 11:33:06
  • 384

CodeForces - 916B(思维+位运算)

链接:CodeForces - 916B 题意: B. Jamie and Binary Sequence (changed after round) time limi...
  • chenquanwei_
  • chenquanwei_
  • 2018-01-20 10:45:18
  • 170
    个人资料
    持之以恒
    等级:
    访问量: 4万+
    积分: 2766
    排名: 1万+
    文章分类
    最新评论