BZOJ - 4034 树上操作 (Dfs序列 + 线段树) || (树链剖分 + 线段树)|| (树状数组)
2017-07-07 17:15
686 查看
大家都很强, 可与之共勉 。
4034: [HAOI2015]树上操作Time Limit: 10 Sec Memory Limit: 256 MB
Description
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
Output
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample Input
5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
Sample Output
6
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
题解 1 : Dfs 序列做法
线段树维护一个长度为2 * n的序列,设2 * n 的序列为c 若当前节点为 u, 则 c [in [u]] = a [u], c [out [u]] = -a[u], 这样从1到in [u] 的c中序列和就是u 到 root的点权和 。
修改时in[u] + delta, out [u] 处 - delta 。
在Build线段树的时候打tag ( 若是in就为1, 反之-1), 维护区间tag 。
然后就是裸裸的线段树区间修改区间查询了 : )
/************************************************************** Problem: 4034 User: Lazer2001 Language: C++ Result: Accepted Time:3228 ms Memory:20128 kb ****************************************************************/ # include <bits/stdc++.h> const int N = 100005 ; typedef long long LL ; int n, m ; int a , aa [N << 1] ; int ev [N << 1] ; int idx, in , out ; std :: vector < int > g ; struct node { bool flag ; int tag ; // for in [], out [] ; LL sum, lazy ; node *ls, *rs ; inline void update ( ) { sum = ls -> sum + rs -> sum ; } inline void pushdown ( int lf, int rg ) { if ( flag ) { ls -> flag = rs -> flag = flag ; ls -> lazy += lazy ; rs -> lazy += lazy ; int mid = ( lf >> 1 ) + ( rg >> 1 ) + ( lf & rg & 1 ) ; ls -> sum += 1LL * ls -> tag * lazy ; rs -> sum += 1LL * rs -> tag * lazy ; lazy = 0 ; flag = false ; } } } pool [N << 2], *tail = pool , *root ; inline void Dfs ( int u, int f ) { aa [in [u] = ++ idx] = a [u] ; ev [idx] = 1 ; for ( int i = 0 ; i < ( int ) g [u].size ( ) ; ++ i ) if ( g [u] [i] ^ f ) Dfs ( g [u] [i], u ) ; aa [out [u] = ++ idx] = -a [u] ; ev [idx] = -1 ; } node* Build ( int lf, int rg ) { node* nd = ++ tail ; if ( lf == rg ) { nd -> sum = aa [lf] ; nd -> tag = ev [lf] ; nd -> lazy = 0 ; nd -> flag = 0 ; return nd ; } int mid = ( lf >> 1 ) + ( rg >> 1 ) + ( lf & rg & 1 ) ; nd -> ls = Build ( lf, mid ) ; nd -> rs = Build ( mid + 1, rg ) ; nd -> tag = nd -> ls -> tag + nd -> rs -> tag ; nd -> update ( ) ; return nd ; } void Modify ( node* &nd, int lf, int rg, int L, int R, int delta ) { if ( L <= lf && rg <= R ) { nd -> lazy += delta ; nd -> flag = true ; nd -> sum += 1LL * nd -> tag * delta ; return ; } nd -> pushdown ( lf, rg ) ; int mid = ( lf >> 1 ) + ( rg >> 1 ) + ( lf & rg & 1 ) ; if ( L <= mid ) Modify ( nd -> ls, lf, mid, L, R, delta ) ; if ( R > mid ) Modify ( nd -> rs, mid + 1, rg, L, R, delta ) ; nd -> update ( ) ; } LL Query ( node* &nd, int lf, int rg, int L, int R ) { if ( L <= lf && rg <= R ) { return nd -> sum ; } LL rt = 0 ; nd -> pushdown ( lf, rg ) ; int mid = ( lf >> 1 ) + ( rg >> 1 ) + ( lf & rg & 1 ) ; if ( L <= mid ) rt += Query ( nd -> ls, lf, mid, L, R ) ; if ( R > mid ) rt += Query ( nd -> rs, mid + 1, rg, L, R ) ; return rt ; // nd -> update ( ) ; } int main ( ) { scanf ( "%d%d", & n, & m ) ; for ( int i = 1 ; i <= n ; ++ i ) scanf ( "%d", a + i ) ; for ( int i = 1 ; i < n ; ++ i ) { static int u, v ; scanf ( "%d%d", & u, & v ) ; g [v].push_back ( u ) ; g [u].push_back ( v ) ; } Dfs ( 1, 0 ) ; root = Build ( 1, idx ) ; while ( m -- ) { static int opt, x, v ; scanf ( "%d", & opt ) ; switch ( opt ) { case 1 : scanf ( "%d%d", & x, & v ) ; Modify ( root, 1, idx, in [x], in [x], v ) ; Modify ( root, 1, idx, out [x], out [x], v ) ; break ; case 2 : scanf ( "%d%d", & x, & v ) ; Modify ( root, 1, idx, in [x], out [x], v ) ; break ; case 3 : scanf ( "%d", & x ) ; printf ( "%lld\n", Query ( root, 1, idx, 1, in [x] ) ) ; break ; } } }
————————————————————————————————
题解 2 : 树链剖分做法待填……
Update : 17.11.3
NOIP之前来填这个啦,至少写熟悉了链剖。
/************************************************************** Problem: 4034 User: Lazer2001 Language: C++ Result: Accepted Time:1532 ms Memory:20376 kb ****************************************************************/ # include <bits/stdc++.h> inline int read ( ) { # define SIZE 2000005 static std :: streambuf *fb ( std :: cin.rdbuf ( ) ) ; static char buf [SIZE], *ss ( 0 ), *tt ( 0 ) ; # define pick( ) ( (ss == tt) ? ( tt = buf + fb -> sgetn ( ss = buf, SIZE ), ((ss == tt) ? -1 : *(ss ++)) ) :*(ss ++) ) register int x, c ; bool opt ( 1 ) ; while ( ! isdigit ( c = pick ( ) ) && ( c ^ -1 ) && ( c ^ 45 ) ) ; if ( c == 45 ) c = pick ( ), opt = 0 ; for ( x = -48 + c ; isdigit ( c = pick ( ) ) ; ( x *= 10 ) += c - 48 ) ; return opt ? x : -x ; # undef pick } # define N 100010 struct node { long long sum ; long long lazy ; bool flag ; node *ls, *rs ; inline void update ( ) { sum = ls -> sum + rs -> sum ; } inline void pushdown ( int l, int r ) { if ( flag ) { ls -> flag = rs -> flag = 1 ; int mid = ( l + r ) >> 1 ; ls -> sum += 1LL * ( mid - l + 1 ) * lazy ; rs -> sum += 1LL * ( r - mid ) * lazy ; ls -> lazy += lazy ; rs -> lazy += lazy ; flag = 0 ; lazy = 0 ; } } } pool [N << 2], *pt = pool, *root ; struct edge { int to ; edge* nxt ; } ; struct Graph { edge g [N << 1], *head ; Graph ( ) { memset ( head, 0, sizeof head ) ; } inline void add_edge ( int u, int v ) { static edge* NewEdge ( g ) ; *( ++ NewEdge ) = ( edge ) { v, head [u] } ; head [u] = NewEdge ; } inline void add_double_edge ( int u, int v ) { add_edge ( u, v ), add_edge ( v, u ) ; } inline edge*& operator [] ( const int& u ) { return head [u] ; } } G ; int n ; int a ; int top , dfn , out , siz , son , fa , dep , seq , idx ; # undef N inline void Dfs1 ( int u, int f ) { siz [u] = 1 ; fa [u] = f ; for ( edge* it = G [u] ; it ; it = it -> nxt ) { int& v = it -> to ; if ( v ^ f ) { dep [v] = dep [u] + 1 ; Dfs1 ( v, u ) ; siz [u] += siz [v] ; if ( siz [u] == 0 || siz [son [u]] < siz [v] ) { son [u] = v ; } } } } inline void Dfs2 ( int u, int tp ) { top [u] = tp, dfn [u] = ++ idx, seq [idx] = u ; if ( son [u] ) Dfs2 ( son [u], tp ) ; for ( edge* it = G [u] ; it ; it = it -> nxt ) { int& v = it -> to ; if ( ( v ^ fa [u] ) && ( v ^ son [u] ) ) { Dfs2 ( v, v ) ; } } out [u] = idx ; } inline node* build ( int lf, int rg ) { node* nd = pt ++ ; nd -> sum = 0, nd -> flag = 0, nd -> lazy = 0 ; if ( lf == rg ) { nd -> sum = a [seq [lf]] ; return nd ; } int mid = ( lf + rg ) >> 1 ; nd -> ls = build ( lf, mid ) ; nd -> rs = build ( mid + 1, rg ) ; nd -> update ( ) ; return nd ; } inline void Modify ( node*& nd, int lf, int rg, int L, int R, int delta ) { if ( L <= lf && rg <= R ) { nd -> sum += 1LL * ( rg - lf + 1 ) * delta ; nd -> flag = 1 ; nd -> lazy += delta ; return ; } nd -> pushdown ( lf, rg ) ; int mid = ( lf + rg ) >> 1 ; if ( L <= mid ) Modify ( nd -> ls, lf, mid, L, R, delta ) ; if ( R > mid ) Modify ( nd -> rs, mid + 1, rg, L, R, delta ) ; nd -> update ( ) ; } inline long long Query ( node*& nd, int lf, int rg, int L, int R ) { if ( L <= lf && rg <= R ) { return nd -> sum ; } nd -> pushdown ( lf, rg ) ; int mid = ( lf + rg ) >> 1 ; long long rt ( 0 ) ; if ( L <= mid ) rt = Query ( nd -> ls, lf, mid, L, R ) ; if ( R > mid ) rt += Query ( nd -> rs, mid + 1, rg, L, R ) ; return rt ; } inline void Modify ( int u, int v, int val ) { while ( top [u] ^ top [v] ) { if ( dep [top [u]] < dep [top [v]] ) u ^= v ^= u ^= v ; Modify ( root, 1, n, dfn [top [u]], dfn [u], val ) ; u = fa [top [u]] ; } if ( dep [u] < dep [v] ) u ^= v ^= u ^= v ; Modify ( root, 1, n, dfn [v], dfn [u], val ) ; } inline long long Query ( int u, int v ) { long long rt ( 0 ) ; while ( top [u] ^ top [v] ) { if ( dep [top [u]] < dep [top [v]] ) u ^= v ^= u ^= v ; rt += Query ( root, 1, n, dfn [top [u]], dfn [u] ) ; u = fa [top [u]] ; } if ( dep [u] < dep [v] ) u ^= v ^= u ^= v ; rt += Query ( root, 1, n, dfn [v], dfn [u] ) ; return rt ; } int main ( ) { std :: ios :: sync_with_stdio ( 0 ) ; std :: cin.tie ( 0 ) ; :: n = read ( ) ; int m ( read ( ) ) ; for ( int i = 1 ; i <= n ; ++ i ) a [i] = read ( ) ; for ( int i = 1 ; i < n ; ++ i ) { G.add_double_edge ( read ( ), read ( ) ) ; } Dfs1 ( 1, 0 ) ; Dfs2 ( 1, 1 ) ; root = build ( 1, n ) ; while ( m -- ) { int opt ( read ( ) ) ; switch ( opt ) { case 1 : { int u ( read ( ) ), val ( read ( ) ) ; Modify ( root, 1, n, dfn [u], dfn [u], val ) ; break ; } case 2 : { int x ( read ( ) ), val ( read ( ) ) ; Modify ( root, 1, n, dfn [x], out [x], val ) ; break ; } case 3 : { int x ( read ( ) ) ; printf ( "%lld\n", Query ( 1, x ) ) ; break ; } } } }
题解 3 : 树状数组做法
/************************************************************** Problem: 4034 User: Lazer2001 Language: C++ Result: Accepted Time:696 ms Memory:7364 kb ****************************************************************/ # include <bits/stdc++.h> char buf [1 << 16], *s ( 0 ), *t ( 0 ) ; inline char pick ( ) { if ( s == t ) t = buf + fread ( s = buf, 1, 1 << 16, stdin ) ; if ( s == t ) return -1 ; return ( *s ++ ) ; } inline void readIn ( int& x ) { register char ch ; static short opt ; opt = ( ch != 45 ) ; while ( ! isdigit ( ch = pick ( ) ) && ( ch ^ -1 ) && ( ch ^ 45 ) ) ; if ( ch == -1 ) return ; if ( ch == 45 ) { opt = 0 ; ch = pick ( ) ; } for ( x = -48 + ch ; isdigit ( ch = pick ( ) ) ; ( x *= 10 ) += ch - 48 ) ; opt ? 1 : x = -x ; } const int N = 100005 ; int a ; int idx, dep , fa , in , out ; struct edge { int to, nxt ; edge ( ) { } edge ( int to, int nxt ) : to ( to ), nxt ( nxt ) { } } g [N << 1] ; int head , ecnt ; inline void Add_edge ( int u, int v ) { g [++ ecnt] = edge ( v, head [u] ) ; head [u] = ecnt ; } class BIT { private : int n ; long long rt, c [100005] ; public : inline void Init ( int n ) { this -> n = n ; // memset ( c, 0, sizeof ( long long ) * ( n + 1 ) ) ; } inline void Modify ( int pos, long long delta ) { while ( pos <= n ) c [pos] += delta, pos += ( pos & -pos ) ; } inline void Modify ( int l, int r, long long delta ) { Modify ( l, delta ) ; Modify ( r + 1, -delta ) ; } inline long long Query ( int pos ) { for ( rt = 0 ; pos ; pos -= ( pos & -pos ) ) rt += c [pos] ; return rt ; } } T1, T2; inline void Dfs ( int u, int f ) { in [u] = ++ idx ; for ( register int i = head [u] ; i ; i = g [i].nxt ) { int v = g [i].to ; if ( v ^ f ) { dep [v] = dep [u] + 1 ; Dfs ( v, u ) ; } } out [u] = idx ; } int main ( ) { int n, m ; readIn ( n ) ; readIn ( m ) ; T1.Init ( n ), T2.Init ( n ) ; for ( register int i = 1 ; i <= n ; ++ i ) readIn ( a [i] ) ; for ( register int i = 1 ; i < n ; ++ i ) { int u, v ; readIn ( u ) ; readIn ( v ) ; Add_edge ( u, v ) ; Add_edge ( v, u ) ; } dep [1] = 1 ; Dfs ( 1, 0 ) ; for ( register int i = 1 ; i <= n ; ++ i ) T1.Modify ( in [i], out [i], a [i] ) ; while ( m -- ) { int opt, x, a ; readIn ( opt ) ; switch ( opt ) { case 1 : readIn ( x ) ; readIn ( a ) ; T1.Modify ( in [x], out [x], a ) ; break ; case 2 : readIn ( x ) ; readIn ( a ) ; T1.Modify ( in [x], out [x], ( long long ) -( dep [x] - 1 ) * a ) ; T2.Modify ( in [x], out [x], a ) ; break ; case 3 : readIn ( x ) ; printf ( "%lld\n", ( long long ) T1.Query ( in [x] ) + ( long long ) T2.Query ( in [x] ) * dep [x] ) ; break ; } } }
相关文章推荐
- 【树链剖分(DFS序)+线段树】BZOJ4034(HAOI2015)[树上操作]题解
- 【BZOJ1103】[POI2007]大都市meg【树链剖分】【线段树】【或 树状数组 + dfs序】
- BZOJ[4034][HAOI2015]树上操作 树链剖分+线段树
- bzoj 4034: [HAOI2015]树上操作(树链剖分+线段树区间更新)
- BZOJ 4034 树上操作 (树链剖分 线段树)
- BZOJ 4034: [HAOI2015]树上操作【树链剖分】【DFS序】
- |BZOJ 4034|树链剖分|线段树|[HAOI2015]树上操作
- 【bzoj4034】树上操作 树链剖分+线段树
- [BZOJ 4034][HAOI 2015] 树上操作 树链剖分+DFS序
- BZOJ 4034: [HAOI2015]树上操作 [欧拉序列 线段树]
- BZOJ 4034 树上操作(树链剖分 + 线段树)
- BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )
- BZOJ-4034: [HAOI2015]树上操作 (树链剖分 入门题 子树整体修改 线段树 区间修改+查询)
- |BZOJ 4034|树链剖分|线段树|[HAOI2015]树上操作
- BZOJ 2434 阿狸的打字机 补全AC自动机 ( Trie图 ) fail树 树状数组 DFS序列
- 【bzoj4034】【HAOI2015】【树上操作】【树链剖分】
- BZOJ 4034 [HAOI2015]树上操作 - 树链剖分
- bzoj 4034: 树上操作 线段树
- 【树链剖分】bzoj4034: [HAOI2015]树上操作
- BZOJ 4034 :[HAOI2015]树上操作 树链剖分裸题