我写的是 DFS序+线段树
DFS序(出去的位置要单独建点)上,进入的位置是权值,出去的位置是权值的相反数,可以证明节点i到根节点的路径上的点的权值和是DFS序上1~in[i]的和。
只要搞出每个区间的进入位置和出去位置的和,就可以打标记了。
1 /************************************************************** 2 Problem: 4034 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:2748 ms 7 Memory:26616 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cstdlib> 12 #define fprintf(...) 13 #define N 100010 14 15 typedef long long dnt; 16 struct Node { 17 dnt s, tag; 18 int tc[2]; 19 Node *ls, *rs; 20 }pool[N*6], *tail=pool, *root; 21 22 int n, m; 23 int head[N], dest[N+N], next[N+N], etot; 24 int in[N], out[N], type[N+N], ww[N], sww[N+N], idc; 25 bool vis[N]; 26 27 Node *build( int lf, int rg ) { 28 Node *nd = ++tail; 29 if( lf==rg ) { 30 nd->s = sww[lf]; 31 nd->tc[type[lf]]=1; 32 nd->tc[type[lf]^1]=0; 33 return nd; 34 } 35 int mid=(lf+rg)>>1; 36 nd->ls = build( lf, mid ); 37 nd->rs = build( mid+1, rg ); 38 nd->tc[0] = nd->ls->tc[0] + nd->rs->tc[0]; 39 nd->tc[1] = nd->ls->tc[1] + nd->rs->tc[1]; 40 nd->s = nd->ls->s + nd->rs->s; 41 fprintf( stderr, "[%d,%d] tc[0]=%d tc[1]=%d ", lf, rg, nd->tc[0], nd->tc[1] ); 42 return nd; 43 } 44 inline void pushdown( Node *nd ) { 45 if( nd->tag ) { 46 nd->ls->s += nd->ls->tc[0]*nd->tag - nd->ls->tc[1]*nd->tag; 47 nd->rs->s += nd->rs->tc[0]*nd->tag - nd->rs->tc[1]*nd->tag; 48 nd->ls->tag += nd->tag; 49 nd->rs->tag += nd->tag; 50 nd->tag = 0; 51 } 52 } 53 inline void update( Node *nd ) { 54 nd->s = nd->ls->s + nd->rs->s; 55 } 56 void modify( Node *nd, int lf, int rg, int L, int R, dnt delta ) { 57 if( L<=lf && rg<=R ) { 58 nd->s += nd->tc[0]*delta - nd->tc[1]*delta; 59 nd->tag += delta; 60 return; 61 } 62 int mid=(lf+rg)>>1; 63 pushdown(nd); 64 if( L<=mid ) modify( nd->ls, lf, mid, L, R, delta ); 65 if( R>mid ) modify( nd->rs, mid+1, rg, L, R, delta ); 66 update( nd ); 67 } 68 dnt query( Node *nd, int lf, int rg, int L, int R ) { 69 if( L<=lf && rg<=R ) { 70 fprintf( stderr, "( %d %d ) = %lld ", lf, rg, nd->s ); 71 return nd->s; 72 } 73 int mid=(lf+rg)>>1; 74 pushdown(nd); 75 dnt rt = 0; 76 if( L<=mid ) rt += query( nd->ls, lf, mid, L, R ); 77 if( R>mid ) rt += query( nd->rs, mid+1, rg, L, R ); 78 update(nd); 79 return rt; 80 } 81 void adde( int u, int v ) { 82 etot++; 83 next[etot] = head[u]; 84 dest[etot] = v; 85 head[u] = etot; 86 } 87 void dfs( int u, int fa ) { 88 if( vis[u] ) { 89 exit(0); 90 return; 91 } 92 vis[u] = true; 93 idc++; 94 in[u] = idc; 95 type[idc] = 0; 96 sww[idc] = ww[u]; 97 for( int t=head[u]; t; t=next[t] ) { 98 int v=dest[t]; 99 if( v==fa ) continue; 100 dfs(v,u); 101 } 102 idc++; 103 out[u] = idc; 104 type[idc] = 1; 105 sww[idc] = -ww[u]; 106 } 107 void mdf_sig( int u, int a ) { 108 modify( root, 1, idc, in[u], in[u], +a ); 109 modify( root, 1, idc, out[u], out[u], +a ); 110 fprintf( stderr, "modify( %d %d %d ) ", in[u], in[u], +a ); 111 fprintf( stderr, "modify( %d %d %d ) ", out[u], out[u], +a ); 112 } 113 void mdf_sub( int u, int a ) { 114 modify( root, 1, idc, in[u], out[u], +a ); 115 fprintf( stderr, "modify( %d %d %d ) ", in[u], out[u], +a ); 116 } 117 dnt query( int u ) { 118 dnt rt = query( root, 1, idc, 1, in[u] ); 119 fprintf( stderr, "query( %d %d ) = %lld ", 1, in[u], rt ); 120 return rt; 121 } 122 int main() { 123 scanf( "%d%d", &n, &m ); 124 for( int i=1; i<=n; i++ ) 125 scanf( "%d", ww+i ); 126 for( int i=1,u,v; i<n; i++ ) { 127 scanf( "%d%d", &u, &v ); 128 adde( u, v ); 129 adde( v, u ); 130 } 131 fprintf( stderr, "dfs(...) " ); 132 dfs(1,1); 133 fprintf( stderr, " " ); 134 fprintf( stderr, "build(...) " ); 135 root = build( 1, idc ); 136 for( int t=0; t<m; t++ ) { 137 int opt, u, a; 138 scanf( "%d", &opt ); 139 fprintf( stderr, "%d ", opt ); 140 if( opt==1 ) { 141 scanf( "%d%d", &u, &a ); 142 mdf_sig( u, a ); 143 } else if( opt==2 ) { 144 scanf( "%d%d", &u, &a ); 145 mdf_sub( u, a ); 146 } else { 147 scanf( "%d", &u ); 148 printf( "%lld ", query(u) ); 149 } 150 } 151 } 152
还有一种做法:链剖
以前一直以为链剖只能用于链修改和链查询。。。。
其实,链剖是一种特殊的DFS序,它合理地安排了DFS的顺序(先重儿子,再轻儿子),让我们可以把任意一条路径映射为O(logn)条连续的线段,然后就可以做很多问题了。
其次是和子树相关的问题,我们一般是把DFS序弄出来,然后一个子树就是连续的一段,这样实现子树相关的操作。
我们把两个结合起来就可以做到:链修改与查询+子树修改与查询。
(好像还有一种链剖解决子树问题的方法,就是每个节点再记录所有轻边代表的子树的信息)。
1 #include <cstdio> 2 #include <cstdlib> 3 #define fprintf(...) 4 #define N 100010 5 6 typedef long long dnt; 7 struct Node { 8 dnt s, tag; 9 int tc[2]; 10 Node *ls, *rs; 11 }pool[N*6], *tail=pool, *root; 12 13 int n, m; 14 int head[N], dest[N+N], next[N+N], etot; 15 int in[N], out[N], type[N+N], ww[N], sww[N+N], idc; 16 bool vis[N]; 17 18 Node *build( int lf, int rg ) { 19 Node *nd = ++tail; 20 if( lf==rg ) { 21 nd->s = sww[lf]; 22 nd->tc[type[lf]]=1; 23 nd->tc[type[lf]^1]=0; 24 return nd; 25 } 26 int mid=(lf+rg)>>1; 27 nd->ls = build( lf, mid ); 28 nd->rs = build( mid+1, rg ); 29 nd->tc[0] = nd->ls->tc[0] + nd->rs->tc[0]; 30 nd->tc[1] = nd->ls->tc[1] + nd->rs->tc[1]; 31 nd->s = nd->ls->s + nd->rs->s; 32 fprintf( stderr, "[%d,%d] tc[0]=%d tc[1]=%d ", lf, rg, nd->tc[0], nd->tc[1] ); 33 return nd; 34 } 35 inline void pushdown( Node *nd ) { 36 if( nd->tag ) { 37 nd->ls->s += nd->ls->tc[0]*nd->tag - nd->ls->tc[1]*nd->tag; 38 nd->rs->s += nd->rs->tc[0]*nd->tag - nd->rs->tc[1]*nd->tag; 39 nd->ls->tag += nd->tag; 40 nd->rs->tag += nd->tag; 41 nd->tag = 0; 42 } 43 } 44 inline void update( Node *nd ) { 45 nd->s = nd->ls->s + nd->rs->s; 46 } 47 void modify( Node *nd, int lf, int rg, int L, int R, dnt delta ) { 48 if( L<=lf && rg<=R ) { 49 nd->s += nd->tc[0]*delta - nd->tc[1]*delta; 50 nd->tag += delta; 51 return; 52 } 53 int mid=(lf+rg)>>1; 54 pushdown(nd); 55 if( L<=mid ) modify( nd->ls, lf, mid, L, R, delta ); 56 if( R>mid ) modify( nd->rs, mid+1, rg, L, R, delta ); 57 update( nd ); 58 } 59 dnt query( Node *nd, int lf, int rg, int L, int R ) { 60 if( L<=lf && rg<=R ) { 61 fprintf( stderr, "( %d %d ) = %lld ", lf, rg, nd->s ); 62 return nd->s; 63 } 64 int mid=(lf+rg)>>1; 65 pushdown(nd); 66 dnt rt = 0; 67 if( L<=mid ) rt += query( nd->ls, lf, mid, L, R ); 68 if( R>mid ) rt += query( nd->rs, mid+1, rg, L, R ); 69 update(nd); 70 return rt; 71 } 72 void adde( int u, int v ) { 73 etot++; 74 next[etot] = head[u]; 75 dest[etot] = v; 76 head[u] = etot; 77 } 78 void dfs( int u, int fa ) { 79 if( vis[u] ) { 80 exit(0); 81 return; 82 } 83 vis[u] = true; 84 idc++; 85 in[u] = idc; 86 type[idc] = 0; 87 sww[idc] = ww[u]; 88 for( int t=head[u]; t; t=next[t] ) { 89 int v=dest[t]; 90 if( v==fa ) continue; 91 dfs(v,u); 92 } 93 idc++; 94 out[u] = idc; 95 type[idc] = 1; 96 sww[idc] = -ww[u]; 97 } 98 void mdf_sig( int u, int a ) { 99 modify( root, 1, idc, in[u], in[u], +a ); 100 modify( root, 1, idc, out[u], out[u], +a ); 101 fprintf( stderr, "modify( %d %d %d ) ", in[u], in[u], +a ); 102 fprintf( stderr, "modify( %d %d %d ) ", out[u], out[u], +a ); 103 } 104 void mdf_sub( int u, int a ) { 105 modify( root, 1, idc, in[u], out[u], +a ); 106 fprintf( stderr, "modify( %d %d %d ) ", in[u], out[u], +a ); 107 } 108 dnt query( int u ) { 109 dnt rt = query( root, 1, idc, 1, in[u] ); 110 fprintf( stderr, "query( %d %d ) = %lld ", 1, in[u], rt ); 111 return rt; 112 } 113 int main() { 114 scanf( "%d%d", &n, &m ); 115 for( int i=1; i<=n; i++ ) 116 scanf( "%d", ww+i ); 117 for( int i=1,u,v; i<n; i++ ) { 118 scanf( "%d%d", &u, &v ); 119 adde( u, v ); 120 adde( v, u ); 121 } 122 fprintf( stderr, "dfs(...) " ); 123 dfs(1,1); 124 fprintf( stderr, " " ); 125 fprintf( stderr, "build(...) " ); 126 root = build( 1, idc ); 127 for( int t=0; t<m; t++ ) { 128 int opt, u, a; 129 scanf( "%d", &opt ); 130 fprintf( stderr, "%d ", opt ); 131 if( opt==1 ) { 132 scanf( "%d%d", &u, &a ); 133 mdf_sig( u, a ); 134 } else if( opt==2 ) { 135 scanf( "%d%d", &u, &a ); 136 mdf_sub( u, a ); 137 } else { 138 scanf( "%d", &u ); 139 printf( "%lld ", query(u) ); 140 } 141 } 142 }