您的位置:首页 > 其它

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 ;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: