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 }

(代码巨慢,应该是分块时拖下来的)

时间: 2024-08-08 03:06:23

bzoj 3757 树上莫队的相关文章

BZOJ - 3757 树上莫队解决离线路径问题 &amp; 学习心得

题意:给你一棵树,求u,v最短路径的XXX(本题是统计权值种类) 今天课上摸鱼学了一种有意思的处理路径方式(其实是链式块状树翻车了看别的),据说实际运行跑的比XX记者还快 大概就是像序列莫队那样 首先是对暴力查询的优化 第一关键字是块(树上分块),第二关键字是dfs序,这样保证了离线操作的下界最优 其次是转移的优化 我把大佬的话再转述一遍: 设\(S(u,v)\):\(u-v\)最短路径所覆盖的点集 \(S(u,v)=S(root,u)⊕S(root,v)⊕lca(u,v)\) 记\(T(u,v

bzoj 3052 树上莫队 待修改

感谢: http://vfleaking.blog.163.com/blog/static/174807634201311011201627/ http://hzwer.com/5250.html 好吧,收获两点: 1.带修改,其实就是暴力,只是将同一块的查询再按照时间顺序排,这样就能减少在修改操作上“爬"的时间,其实就是利用了数据随机这个特点,可以构造数据来卡. 2.以前排序的方法是u按照块,v按照dfs序,这次两个都是按照块,其实差不多. 1 /***********************

bzoj 3757: 苹果树(树上莫队)

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1327  Solved: 510 [Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个1到n之间的正整数来表示一种颜色

BZOJ 3757 苹果树 树上莫队

题目大意:给出一棵树,问任意两点之间有多少种不同的颜色,一个人可能会有色盲,会将A和B当成一种颜色. 思路:比较裸的树上莫队,写出来之后,很慢,怀疑是分块的缘故,然后果断找了当年比赛的标称交上去,瞬间rk1,大概看了一眼,他好像是直接用DFS序+曼哈顿距离最小生成树搞的,为什么会比分块快? 昨天下午看到这个题之后就一直在研究树上莫队的正确姿势,然后先写了树分块,后来看了很多牛人的SPOJ COT2的题解,后来又和同学探讨了好久才弄明白. 首先先将树分块,然后把区间排序,按照第一权值为左端点所在块

[BZOJ 3052] [wc2013] 糖果公园 【树上莫队】

题目链接:BZOJ - 3052 题目分析 这道题就是非常经典的树上莫队了,并且是带修改的莫队. 带修改的莫队:将询问按照 左端点所在的块编号为第一关键字,右端点所在的块为第二关键字,位于第几次修改之后为第三关键字 排序. 我们将块的大小设置在 n^(2/3) ,这样一共有 n^(1/3) 个块.最后算法的总复杂度会是 n^(5/3) . 每一次如何从上一个询问转移来呢? 假设上一个询问是 (lx, ly, lt) ,这次的询问是 (x, y, t) .t 代表是在第 t 次修改操作之后. 首先

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了. 关于树链的滑动,只需要特殊处理一下$LCA$就行了. 在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数.因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$. 转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移. 这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫

【BZOJ-3757】苹果树 块状树 + 树上莫队

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1305  Solved: 503[Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个到n之间的正整数来表示一种颜色.树上

(树上莫队)HDU - 5799 This world need more Zhu

题意: 两种询问: 1.询问以u为根的子树中出现的a次的数的和与出现b次的数的和的gcd. 2.询问u到v的树链中出现的a次的数的和与出现b次的数的和的gcd. 有点绕.. 分析: 因为自己不会树上莫队,所以学习了一波. 但是对于子树我还是有所经验,可以转成dfs序来做,之前有做过类似的题,比如这题. 然而对于树链有点懵逼,虽然我觉得也能用dfs序做,不过看大佬们的dfs序做的树链查询,也有点懵,感觉写起来很麻烦. 貌似是修改了dfs序,回溯的时候不再只是和进入时相同的序,而是独立的序. 还是感

[spoj COT2]树上莫队

题目链接:http://www.spoj.com/problems/COT2/ 学会了树上莫队,真的是太激动了!参照博客:http://codeforces.com/blog/entry/43230 讲的十分清楚. #include<bits/stdc++.h> using namespace std; const int MAXN=40005; const int maxm=100005; int cur; int sat[MAXN]; int ean[MAXN]; int A[MAXN*2