bzoj 3772
感觉做这种题收获很大。
1、DFS序(广义上)除了用于静态子树操作,也可以用来做点到根的路上某些信息的统计(如点到根的路径上标记了多少个点),如果在加上lca,就可以支持路径的信息查询。
2、树上的可持久化线段树,如果每个节点要维护一个线段树,并且该线段树支持加减操作,那么通过可持久化+lca,搞定一条路径上的线段树的和(当然不仅局限于线段树)。
3、一条树上的路径覆盖另一条路径 <=> 后者的两个端点在前者的路径上。
题解看PoPoQQQ的博客:
http://blog.csdn.net/popoqqq/article/details/43122821
如果不清楚就看看代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /************************************************************** 2 Problem: 3772 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:5824 ms 7 Memory:65180 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <vector> 12 #define N 100010 13 #define S 4000000 14 #define P 16 15 using namespace std; 16 17 typedef long long dnt; 18 19 struct Node { 20 int s; 21 Node *ls, *rs; 22 }pool[S], *tail=pool, *root[N], *null=pool; 23 struct Qry { 24 int u, v; 25 Qry(){} 26 Qry( int u, int v ):u(u),v(v){} 27 }; 28 29 int n, m; 30 int head[N], dest[N+N], next[N+N], ntot; 31 int in[N], out[N], idc; 32 int anc[N][P+1], dep[N]; 33 vector<int> vc[N]; 34 Qry qry[N]; 35 36 void insert( int u, int v ) { 37 ntot++; 38 next[ntot] = head[u]; 39 dest[ntot] = v; 40 head[u] = ntot; 41 } 42 Node *modify( Node *nd, int lf, int rg, int pos, int delta ) { 43 Node *rt = ++tail; 44 if( lf==rg ) { 45 rt->s = nd->s + delta; 46 return rt; 47 } 48 int mid=(lf+rg)>>1; 49 if( pos<=mid ) { 50 rt->ls = modify( nd->ls, lf, mid, pos, delta ); 51 rt->rs = nd->rs; 52 } else { 53 rt->ls = nd->ls; 54 rt->rs = modify( nd->rs, mid+1, rg, pos, delta ); 55 } 56 rt->s = rt->ls->s + rt->rs->s; 57 return rt; 58 } 59 int query( Node *nd, int lf, int rg, int L, int R ) { 60 if( L<=lf && rg<=R ) return nd->s; 61 int mid=(lf+rg)>>1; 62 int rt = 0; 63 if( L<=mid ) rt += query( nd->ls, lf, mid, L, R ); 64 if( R>mid ) rt += query( nd->rs, mid+1, rg, L, R ); 65 return rt; 66 } 67 int query( Node *p1, Node *p2, Node *p3, Node *p4, int L, int R ) { 68 /* (p1+p2-p3-p4) as one tree */ 69 int s1, s2, s3, s4; 70 s1 = query(p1,1,idc,L,R); 71 s2 = query(p2,1,idc,L,R); 72 s3 = query(p3,1,idc,L,R); 73 s4 = query(p4,1,idc,L,R); 74 return s1+s2-s3-s4; 75 } 76 void dfs1( int u ) { 77 in[u] = ++idc; 78 for( int p=1; p<=P; p++ ) 79 anc[u][p] = anc[anc[u][p-1]][p-1]; 80 for( int t=head[u]; t; t=next[t] ) { 81 int v=dest[t]; 82 if( v==anc[u][0] ) continue; 83 anc[v][0] = u; 84 dep[v] = dep[u]+1; 85 dfs1(v); 86 } 87 out[u] = ++idc; 88 } 89 void dfs2( int u ) { 90 root[u] = root[anc[u][0]]; 91 for( int t=0; t<vc[u].size(); t++ ) { 92 int v=vc[u][t]; 93 root[u] = modify( root[u], 1, idc, in[v], +1 ); 94 root[u] = modify( root[u], 1, idc, out[v], -1 ); 95 } 96 for( int t=head[u]; t; t=next[t] ) { 97 int v=dest[t]; 98 if( v==anc[u][0] ) continue; 99 dfs2(v); 100 } 101 } 102 int lca( int u, int v ) { 103 if( dep[u]<dep[v] ) swap(u,v); 104 int t=dep[u]-dep[v]; 105 for( int p=0; t; t>>=1,p++ ) 106 if( t&1 ) u=anc[u][p]; 107 if( u==v ) return u; 108 for( int p=P; p>=0 && anc[u][0]!=anc[v][0]; p-- ) 109 if( anc[u][p]!=anc[v][p] ) 110 u=anc[u][p], v=anc[v][p]; 111 return anc[u][0]; 112 } 113 dnt gcd( dnt a, dnt b ) { 114 return b ? gcd(b,a%b) : a; 115 } 116 int main() { 117 scanf( "%d%d", &n, &m ); 118 for( int i=1,u,v; i<n; i++ ) { 119 scanf( "%d%d", &u, &v ); 120 insert( u, v ); 121 insert( v, u ); 122 } 123 for( int i=1,u,v; i<=m; i++ ) { 124 scanf( "%d%d", &u, &v ); 125 vc[u].push_back(v); 126 qry[i] = Qry(u,v); 127 } 128 129 anc[1][0] = 0; 130 dep[1] = 1; 131 dfs1(1); 132 133 null->ls = null->rs = null; 134 root[0] = null; 135 dfs2(1); 136 137 dnt cnt = 0, tot = 0, cd = 0; 138 for( int i=1; i<=m; i++ ) { 139 int u=qry[i].u, v=qry[i].v, ca=lca(u,v); 140 cnt += query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[u] ); 141 cnt += query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[v] ); 142 cnt -= query( root[u], root[v], root[ca], root[anc[ca][0]], in[ca], in[ca] ); 143 cnt --; 144 } 145 tot = (dnt)m*(m-1)/2; 146 cd = gcd(tot,cnt); 147 printf( "%lld/%lld\n", cnt/cd, tot/cd ); 148 }