bzoj 3757 树上莫队
感谢以下文章作者:
http://blog.csdn.net/kuribohg/article/details/41458639
http://vfleaking.blog.163.com/blog/static/174807634201311011201627/
http://blog.csdn.net/jiangyuze831/article/details/41476865
http://hzwer.com/5259.html
做了树上的莫队,感觉对这个算法的思想理解更深了
先分块,不论怎么分,要求同一块中的节点之间的距离不超过O(n0.5)
然后将图的DFS序搞出来
然后将询问(u,v),以u所在的块为第一关键字,以v在dfs序中的位置为第二关键字排序。
然后弄个初始状态,然后就在图上按照询问的顺序“爬”(从(u,v)的状态转移到(u',v'),细节见vfleaking文章)。
至于复杂度,和序列型莫队的分析是一样的,我们的时间开销主要花在“爬”上,我们将爬的开销分成两部分来算:
第一部分:(u,v)中u改变造成的开销,如果在同一块中转移,我们最多需要走O(n0.5)步,要走O(n)次;如果在块间转移,我们最多走O(n)步,要走O(n0.5)次。总的O(n1.5)
第二部分:(u,v)中v改变造成的开销,同一块中的所有点总的开销是O(n)(同一块中的v是按dfs序排的序),有O(n0.5)块,所以是O(n1.5);不同块间走O(n0.5)次,每次O(n),总的也是O(n1.5)
所以总的是O(n1.5)。
这题没说m的范围,开成和n一样要RE,记得开成它的两倍。
1 /************************************************************** 2 Problem: 3757 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:17840 ms 7 Memory:16640 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cmath> 12 #include <vector> 13 #include <algorithm> 14 #define P(p) (1<<(p)) 15 #define maxn 100010 16 #define maxp 15 17 using namespace std; 18 19 20 int n, m; 21 int root; 22 vector<int> g[maxn]; 23 int stat[maxn], clr[maxn], cnt[maxn], clr_tot; 24 int anc[maxn][maxp+1], depth[maxn], dfn[maxn], dfs_clock; 25 int mccno[maxn], mcc_len, mcc_tot; 26 int ans[maxn]; 27 28 struct Qu { 29 int u, v, id; 30 int a, b; 31 bool operator<( const Qu & c ) const { 32 return mccno[u]<mccno[c.u] || ( mccno[u]==mccno[c.u] && dfn[v]<dfn[c.v] ); 33 } 34 }; 35 Qu qry[maxn]; 36 37 void dfs( int u, int fa, vector<int> &remain ) { 38 dfn[u] = ++dfs_clock; 39 anc[u][0] = fa; 40 for( int p=1; p<=maxp; p++ ) 41 anc[u][p] = anc[anc[u][p-1]][p-1]; 42 depth[u] = depth[fa]+1; 43 vector<int> cur; 44 for( int t=0; t<g[u].size(); t++ ) { 45 int v = g[u][t]; 46 if( v==fa ) continue; 47 dfs(v,u,cur); 48 if( cur.size()>mcc_len ) { 49 mcc_tot++; 50 while( !cur.empty() ) { 51 mccno[ cur.back() ] = mcc_tot; 52 cur.pop_back(); 53 } 54 } 55 } 56 while( !cur.empty() ) { 57 remain.push_back( cur.back() ); 58 cur.pop_back(); 59 } 60 remain.push_back(u); 61 } 62 63 int lca( int u, int v ) { 64 if( depth[u]<depth[v] ) swap(u,v); 65 int t = depth[u]-depth[v]; 66 for( int p=maxp; p>=0 && t; p-- ) 67 if( t&(P(p)) ) u = anc[u][p]; 68 if( u==v ) return u; 69 for( int p=maxp; p>=0 && anc[u][0]!=anc[v][0]; p-- ) 70 if( anc[u][p]!=anc[v][p] ) u = anc[u][p], v = anc[v][p]; 71 return anc[u][0]; 72 } 73 74 void inv_sig( int u ) { 75 int c = clr[u]; 76 int d = stat[u] ? -1 : 1; 77 stat[u] ^= 1; 78 if( cnt[c]==0 ) clr_tot++; 79 cnt[c] += d; 80 if( cnt[c]==0 ) clr_tot--; 81 } 82 void inverse( int u, int v ) { // inverse T(u,v) 83 int ca = lca(u,v); 84 for( ; u!=ca; u=anc[u][0] ) 85 inv_sig(u); 86 for( ; v!=ca; v=anc[v][0] ) 87 inv_sig(v); 88 } 89 void calc( int q ) { 90 inverse( qry[q-1].u, qry[q].u ); 91 inverse( qry[q-1].v, qry[q].v ); 92 int ca = lca( qry[q].u, qry[q].v ); 93 94 inv_sig( ca ); 95 ans[qry[q].id] = clr_tot; 96 if( qry[q].a != qry[q].b && cnt[qry[q].a] && cnt[qry[q].b] ) 97 ans[qry[q].id]--; 98 inv_sig( ca ); 99 } 100 101 int main() { 102 scanf( "%d%d", &n, &m ); 103 mcc_len = (int)sqrt(n+1); 104 for( int i=1; i<=n; i++ ) 105 scanf( "%d", clr+i ); 106 for( int i=1,u,v; i<=n; i++ ) { 107 scanf( "%d%d", &u, &v ); 108 if( u==0 ) root=v; 109 if( v==0 ) root=u; 110 if( u&&v ) { 111 g[u].push_back( v ); 112 g[v].push_back( u ); 113 } 114 } 115 for( int i=1; i<=m; i++ ) { 116 scanf( "%d%d%d%d", &qry[i].u, &qry[i].v, &qry[i].a, &qry[i].b ); 117 qry[i].id = i; 118 } 119 120 vector<int> remain; 121 dfs( root, root, remain ); 122 while( remain.size() ) { 123 mccno[ remain.back() ] = mcc_tot; 124 remain.pop_back(); 125 } 126 127 sort( qry+1, qry+1+m ); 128 129 qry[0].u = qry[0].v = qry[1].u; 130 for( int i=1; i<=m; i++ ) 131 calc(i); 132 for( int i=1; i<=m; i++ ) 133 printf( "%d\n", ans[i] ); 134 }
(代码巨慢,应该是分块时拖下来的)