CF1009F Dominant Indices(树上启发式合并)

这道题用到了dsu(Disjoint Set Union) on tree,树上启发式合并。

先看了CF的官方英文题解,又看了看zwz大佬的题解,差不多理解了dsu on tree的算法。

但是时间复杂度有点玄学,问了一下zwz大佬才懂了为什么是nlogn。

题目传送门

先考虑暴力n^2的算法。

显然对于某个点,搜一遍它的子树,就能得到这个点的答案。这一步是O(n)的。

每个点都这么搞一遍,就是O(n^2)的暴力做法。

但是这个暴力做法有一点不足,子节点的答案没有应用到父节点的计算中,白白浪费时间重算一遍。

考虑优化,类似树链剖分,找出子树最大的儿子称作重儿子,把它的答案留着,这样计算父节点时就不用搜这个重儿子了。

显然保留子树最大的儿子的信息,能够节约最多的时间。

但是如果计算完重儿子的答案,保留了信息,再计算别的儿子的答案,已保留的信息会对当前的计算产生干扰。

所以我们先计算轻儿子,最后计算重儿子。

如果是轻儿子,更新答案计算后,暴力再改回去。

如果是重儿子,就留着。

计算完所有儿子的答案后,最后计算当前点。

只需要加上轻儿子的信息就好。重儿子的信息已经留着了,不用再加了。

下面是zwz大佬对于dsu on tree时间复杂度的证明:

每个节点只会在祖先节点的计算中被搜到。

而且只有它到它父亲是轻边的时候才会搜一遍。

所以每个点的计算次数是它到根的轻边数量,为logn。

所以总时间复杂度是nlogn。

感觉dsu也是挺暴力的,每次留一个,居然时间上优化了很多。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<queue>
  5 #define MAXN 1000005
  6 using namespace std;
  7 
  8 int n;
  9 int hd[MAXN],nx[MAXN<<1],to[MAXN<<1],ec;
 10 int dep[MAXN],sz[MAXN],ans[MAXN],cnt[MAXN];
 11 
 12 void edge(int af,int at)
 13 {
 14     to[++ec]=at;
 15     nx[ec]=hd[af];
 16     hd[af]=ec;
 17 }
 18 
 19 void pre(int p,int fa)
 20 {
 21     sz[p]=1;
 22     dep[p]=dep[fa]+1;
 23     for(int i=hd[p];i;i=nx[i])
 24     {
 25         if(to[i]==fa)continue;
 26         pre(to[i],p);
 27         sz[p]+=sz[to[i]];
 28     }
 29 }
 30 
 31 struct data
 32 {
 33     int d,v;
 34     friend bool operator<(data q,data w)
 35     {
 36         if(q.v==w.v)return q.d>w.d;
 37         return q.v<w.v;
 38     }
 39 };
 40 
 41 priority_queue<data>qq;
 42 
 43 void add(int p,int fa)
 44 {
 45     cnt[dep[p]]++;
 46     data neo={dep[p],cnt[dep[p]]};
 47     qq.push(neo);
 48     for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)add(to[i],p);
 49 }
 50 
 51 void del(int p,int fa)
 52 {
 53     cnt[dep[p]]--;
 54     for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)del(to[i],p);
 55 }
 56 
 57 void dfs(int p,int fa,int stay)
 58 {
 59     int son=0,mx=-1;
 60     for(int i=hd[p];i;i=nx[i])
 61     {
 62         if(to[i]==fa)continue;
 63         if(mx<sz[to[i]])mx=sz[to[i]],son=to[i];
 64     }
 65     for(int i=hd[p];i;i=nx[i])
 66     {
 67         if(to[i]==fa||to[i]==son)continue;
 68         dfs(to[i],p,0);
 69     }
 70     if(son)dfs(son,p,1);
 71     for(int i=hd[p];i;i=nx[i])
 72     {
 73         if(to[i]==fa||to[i]==son)continue;
 74         add(to[i],p);
 75     }
 76     cnt[dep[p]]++;
 77     data neo={dep[p],cnt[dep[p]]};
 78     qq.push(neo);
 79     ans[p]=qq.top().d-dep[p];
 80     if(!stay)
 81     {
 82         del(p,fa);
 83         while(!qq.empty())qq.pop();
 84     }
 85 }
 86 
 87 int main()
 88 {
 89     scanf("%d",&n);
 90     for(int i=1;i<n;i++)
 91     {
 92         int x,y;
 93         scanf("%d%d",&x,&y);
 94         edge(x,y);
 95         edge(y,x);
 96     }
 97     pre(1,0);
 98     dfs(1,0,1);
 99     for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
100     return 0;
101 }
CF1009F Dominant Indices

P.S. 调试的时候改小了数组,提交的时候忘改回去了......改回去之后直接A掉了......有点桑心哈哈哈

猜你喜欢

转载自www.cnblogs.com/eternhope/p/9556766.html