CF1009F Dominant Indices(树上DSU/长链剖分)

题目大意:

就是给你一棵以1为根的树,询问每一个节点的子树内节点数最多的深度(相对于这个子树根而言)若有多解,输出最小的。

解题思路:

这道题用树链剖分,两种思路:

1.树上DSU

首先想一下最暴力的算法:统计子树每个深度节点的个数(桶)相当于以每个节点为根遍历子树搜索一遍答案,这样做时间复杂度是O(n2),显然过不去。

考虑一下优化。假如说我们模拟一下搜索答案的过程,我们发现在每一次暴搜时都会在桶中添加一些答案。而这些答案的整体只会对该节点及其祖先产生贡献,也就是说,只有该节点以及其祖先的桶中才一定会有这个答案的整体,也只会对该节点及以下非同祖子树答案产生干扰。也就是说,搜索一棵树时,其答案可以直接加到其祖先上,所以对于最后一颗子树,因为之后不会再干扰其他子树所以其答案可以直接上传至父节点。那么这个最后搜索的节点的子树应该越大越好,那么就可以使用树链剖分解决了。答案更新时,最后搜最大的即可。

对于时间复杂度,每次搜索最坏为O(n),最大儿子上传是O(1),小儿子上传为O(n),轻儿子log2n个所以时间复杂度为O(logn)

上代码:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 struct pnt{
  5     int hd;
  6     int dp;
  7     int wgt;
  8     int mxs;
  9     int ans;
 10 }p[3000000];
 11 struct ent{
 12     int twd;
 13     int lst;
 14 }e[3000000];
 15 struct pr{
 16     int x,y;
 17     bool friend operator == (pr a,pr b){return (a.x==b.x)&&(a.y==b.y);}
 18     bool friend operator > (pr a,pr b){if(a.y==b.y)return a.x<b.x;return a.y>b.y;}
 19     bool friend operator < (pr a,pr b){if(a.y==b.y)return a.x>b.x;return a.y<b.y;}
 20     bool friend operator <= (pr a,pr b){return ((a<b)||(a==b));}
 21     bool friend operator >= (pr a,pr b){return ((a>b)||(a==b));}
 22 };
 23 class Prq{
 24     public:
 25         bool bol(void)
 26         {
 27             return (bool)(siz==0); 
 28         }
 29         void maxs(int &b)
 30         {
 31             b=line[1].x;
 32         }
 33         void del(void)
 34         {
 35             line[1]=line[siz--];
 36             int nw,nx;
 37             nw=1;
 38             while((nw<<1)<=siz)
 39             {
 40                 nx=nw<<1;
 41                 if(nx<siz&&line[nx]<line[nx+1])
 42                     nx++;
 43                 if(line[nx]<=line[nw])
 44                     break;
 45                 std::swap(line[nw],line[nx]);
 46                 nw=nx;
 47             }
 48             return ;
 49         }
 50         void ins(int a,int b)
 51         {
 52             pr tmp=(pr){a,b};
 53             line[++siz]=tmp;
 54             int nx,nw;
 55             nw=siz;
 56             while(nw>1)
 57             {
 58                 nx=nw>>1;
 59                 if(line[nx]>=line[nw])
 60                     break;
 61                 std::swap(line[nx],line[nw]);
 62                 nw=nx;
 63             }
 64             return ;
 65         }
 66         void dst(void)
 67         {
 68             siz=0;
 69             return ;
 70         }
 71     private:
 72         pr line[10000000];
 73         int siz;
 74 }Q;
 75 int n;
 76 int cnt;
 77 int num[2000001];
 78 void ade(int f,int t)
 79 {
 80     cnt++;
 81     e[cnt].twd=t;
 82     e[cnt].lst=p[f].hd;
 83     p[f].hd=cnt;
 84 }
 85 void Basic_dfs(int x,int f)
 86 {
 87     p[x].dp=p[f].dp+1;
 88     p[x].wgt=1;
 89     int maxs=-1;
 90     for(int i=p[x].hd;i;i=e[i].lst)
 91     {
 92         int to=e[i].twd;
 93         if(to==f)
 94             continue;
 95         Basic_dfs(to,x);
 96         p[x].wgt+=p[to].wgt;
 97         if(maxs<p[to].wgt)
 98         {
 99             maxs=p[to].wgt;
100             p[x].mxs=to;
101         }
102     }
103 }
104 void Build_dfs(int x,int f)
105 {
106     num[p[x].dp]++;
107     Q.ins(p[x].dp,num[p[x].dp]);
108     for(int i=p[x].hd;i;i=e[i].lst)
109     {
110         int to=e[i].twd;
111         if(to==f)
112             continue;
113         Build_dfs(to,x);
114     }
115 }
116 void Destory_dfs(int x,int f)
117 {
118     num[p[x].dp]--;
119     for(int i=p[x].hd;i;i=e[i].lst)
120     {
121         int to=e[i].twd;
122         if(to==f)
123             continue;
124         Destory_dfs(to,x);
125     }
126 }
127 void DSU_dfs(int x,int f,bool hvs)
128 {
129     if(!x)
130         return ;
131     for(int i=p[x].hd;i;i=e[i].lst)
132     {
133         int to=e[i].twd;
134         if(to==f||to==p[x].mxs)
135             continue;
136         DSU_dfs(to,x,false);
137     }
138     DSU_dfs(p[x].mxs,x,true);
139     for(int i=p[x].hd;i;i=e[i].lst)
140     {
141         int to=e[i].twd;
142         if(to==f||to==p[x].mxs)
143             continue;
144         Build_dfs(to,x);
145     }
146     num[p[x].dp]++;    
147     Q.ins(p[x].dp,num[p[x].dp]);
148     Q.maxs(p[x].ans);
149     p[x].ans-=p[x].dp;
150     if(hvs)
151         return ;
152     Destory_dfs(x,f);
153     Q.dst();
154     return ;
155 }
156 int main()
157 {
158     scanf("%d",&n);
159     for(int i=1;i<n;i++)
160     {
161         int x,y;
162         scanf("%d%d",&x,&y);
163         ade(x,y);
164         ade(y,x);
165     }
166     Basic_dfs(1,1);
167     DSU_dfs(1,1,true);
168     for(int i=1;i<=n;i++)
169         printf("%d
",p[i].ans);
170     return 0;
171 }

2.树的长链剖分:

这个方法比上面的方法跑得快。

首先,观察那种最朴素的全搜一遍的O(n2)算法,它的瓶颈在于,统计答案时同一深度非同父的节点,其答案可能互相干扰,那么我们为何不合理安排内存使其答案不会互相被访问到而会同时被祖先访问到。类似于一个树链剖分序。类似长链先搜,短链后搜的剖分序。使用不同的下标索引使桶中的变量不会在深度环境下发生冲突,再logn统计答案就可以了,其实是O(nlogn)但重建部分比较简单常数较小且其最坏复杂度很难达到所以速度相当惊人。

上代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 struct pnt{
 5     int hd;
 6     int dp;
 7     int mxs;
 8     bool vis;
 9     int ind;
10     int ans;
11     int wsa;
12 }p[1000001];
13 struct ent{
14     int twd;
15     int lst;
16 }e[10000001];
17 int n,m;
18 int cnt;
19 int wh;
20 int tmp[1000001];
21 void ade(int f,int t)
22 {
23     cnt++;
24     e[cnt].twd=t;
25     e[cnt].lst=p[f].hd;
26     p[f].hd=cnt;
27 }
28 void Basic_dfs(int x,int f)
29 {
30     p[x].dp=p[f].dp+1;
31     p[x].wsa=p[x].dp;
32     for(int i=p[x].hd;i;i=e[i].lst)
33     {
34         int to=e[i].twd;
35         if(to==f)
36             continue;
37         Basic_dfs(to,x);
38         p[x].wsa=std::max(p[x].wsa,p[to].wsa);
39         if(p[to].wsa>p[p[x].mxs].wsa)
40         {
41             p[x].mxs=to;
42         }
43     }
44 }
45 void Gund_dfs(int x,int f)
46 {
47     tmp[p[x].ind]=1;
48     if(p[x].mxs)
49     {
50         p[p[x].mxs].ind=p[x].ind+1;
51         Gund_dfs(p[x].mxs,x);
52         p[x].ans=p[p[x].mxs].ans+1;
53     }
54     for(int i=p[x].hd;i;i=e[i].lst)
55     {
56         int to=e[i].twd;
57         if(to==f||to==p[x].mxs)
58             continue;
59         p[to].ind=wh;
60         wh+=p[to].wsa-p[to].dp+1;
61         Gund_dfs(to,x);
62         for(int j=0;j<=p[to].wsa-p[to].dp;j++)
63         {
64             tmp[p[x].ind+j+1]+=tmp[p[to].ind+j];
65             if(tmp[p[x].ind+j+1]>tmp[p[x].ind+p[x].ans]||(tmp[p[x].ind+j+1]==tmp[p[x].ind+p[x].ans]&&p[x].ans>j+1))
66                 p[x].ans=j+1;
67         }
68     }
69     if(tmp[p[x].ind+p[x].ans]==1)
70         p[x].ans=0;
71 }
72 int main()
73 {
74     scanf("%d",&n);
75     for(int i=1;i<n;i++)
76     {
77         int x,y;
78         scanf("%d%d",&x,&y);
79         ade(x,y);
80         ade(y,x);
81     }
82     Basic_dfs(1,1);
83     wh=p[1].wsa;
84     Gund_dfs(1,1);
85     for(int i=1;i<=n;i++)
86         printf("%d
",p[i].ans);
87     return 0;
88 }
原文地址:https://www.cnblogs.com/blog-Dr-J/p/9557775.html