计蒜客 444 / xtuoj 1024 京东的物流路径(并查集+离线lca)或者 (点分治)

题意:
一颗树,定义一条路径的权值等于路径的边权之和,需要求这颗树所有路径中权值的最大值

思路:

考虑到路径权值与点权的最值有关,而最值的问题通常可以通过排序就行处理,于是想到先把点权排序。

容易看出如果某条路径的权值是通过某个点算出的最小 ,那么肯定这条路径肯定不会经过权值更小的点,于是有了两种处理思路

1.按点权从小到大删点,对于即将删除的点,比他权值小的点已经被删去了,所以只要在当前状态的森林里找一条最长路径乘以次点权就可以更新答案

2.按点权从大到小加点,显然新加进来的点权值最小,当前树里的任何路径的点权最小值都不会小于新加进来的点,所以可以通过维护直径来更新答案

对于思路1,如果是一条链的话,对于一个将要删除的点,可以直接二分得到当前点附近已经被删除的点并更新答案,然而在树里面就不太好从处理了,至少我肯定是不会的

于是考虑思路2:

加点建树很明显可以通过并查集维护

但是在树的合并过程中怎么维护直径呢?这里需要用到一个定理。。一颗树的直径的两个端点一定是他子树直径的端点

于是就可以进行直径的维护了,具体求距离可以选择logn的lca

代码:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 long long f[200010];
  4 // 节点编号[1,n]
  5 // 最大节点数
  6 #define MAXN 200010
  7 // 满足2^M > MAXN
  8 #define M 20
  9 struct node
 10 {
 11     long long to,val;
 12 };
 13 struct tree
 14 {
 15     long long r,x,y;
 16     void clear()
 17     {
 18         r=0;
 19         x=y=-1;
 20     }
 21 }t[100010];
 22 vector<node>g[200010];
 23 long long d[200010];
 24 long long n;
 25 int a[200010];
 26 long long ans;
 27 long long ch[MAXN]={0}; // 节点i下面的孩子的个数(包括i)
 28 long long fa[MAXN][M+2]; // ch[i][j]表示与节点i距离为2^j的i的祖先
 29 long long len[MAXN][M+2];
 30 long long deep[MAXN]={0}; // i的深度(根节点的深度为1)
 31 // 初始化deep数组,ch数组,fa数组的初始值
 32 // 默认根节点为1
 33 long long dfs(int cur=1, int father = 0,long long val=0)
 34 {
 35     deep[cur] = deep[father]+1;
 36     fa[cur][0] = father;
 37     len[cur][0]=val;
 38     ch[cur] = 1;
 39     int sz = g[cur].size();
 40     for(int i=0;i<sz;i++)
 41     {
 42         if(g[cur][i].to != father)
 43             ch[cur] += dfs(g[cur][i].to,cur,g[cur][i].val);
 44     }
 45     return ch[cur];
 46 }
 47 // 初始化fa数组
 48 void initFa(int n)
 49 {
 50     for(int i=1;i<=19;i++)
 51         for(int node=1;node<=n;node++)
 52         {
 53             fa[node][i] = fa[fa[node][i-1]][i-1];
 54             len[node][i]= len[fa[node][i-1]][i-1]+len[node][i-1];
 55         }
 56 }
 57 // 将node上升height的高度
 58 int binaryRaise(int node, int height,long long &val)
 59 {
 60     val=0;
 61     for(int i=M; i>=0; i--)
 62     {
 63         if(fa[node][i] && height >= (1<<i))
 64         {
 65             val+=len[node][i];
 66             node = fa[node][i];
 67             height -= (1<<i);
 68         }
 69     }
 70     return node;
 71 }
 72 // a的深度比b大
 73 long long lca(int a, int b)
 74 {
 75     if(a==0||b==0)
 76         return -1;
 77     // 先移动a到与b同样深度的地方
 78     if(deep[a]<deep[b])
 79         swap(a,b);
 80     long long res=0;
 81     a = binaryRaise(a, deep[a]-deep[b],res);
 82     if(a==b) // 此时,b就是a和b的公共祖先
 83         //return a;
 84         return res;
 85     for(int i=M;i>=0;i--)
 86     {
 87         if(a!=b && fa[a][i]!=fa[b][i])
 88         {
 89             res+=len[a][i];
 90             a = fa[a][i];
 91             res+=len[b][i];
 92             b = fa[b][i];
 93         }
 94     }
 95 
 96     return res+len[a][0]+len[b][0];
 97 }
 98 int find(int a)
 99 {
100     if(f[a]==a||f[a]==-1)
101         return f[a]=a;
102     f[a]=find(f[f[a]]);
103     t[a]=t[f[a]];
104     return f[a];
105 }
106 int uni(int a,int b)
107 {
108     int x=find(a),y=find(b);
109     return f[y]=x;
110 }
111 bool issame(int a,int b)
112 {
113     return find(a)==find(b);
114 }
115 
116 bool cmp(int a,int b)
117 {
118     return d[a]>d[b];
119 }
120 void merg(int a,int b)
121 {
122     int fb=find(b);
123     long long l11=lca(t[a].x,t[fb].x);
124     long long l12=lca(t[a].x,t[fb].y);
125     long long l21=lca(t[a].y,t[fb].x);
126     long long l22=lca(t[a].y,t[fb].y);
127     long long maxl=max(max(max(max(max(l11,l12),l21),l22),t[a].r),t[fb].r);
128     int x1=t[a].x;
129     int x2=t[fb].x;
130     int y1=t[a].y;
131     int y2=t[fb].y;
132     if(l11==maxl)
133     {
134         t[a].x=x1;
135         t[a].y=x2;
136     }
137     if(l12==maxl)
138     {
139         t[a].x=x1;
140         t[a].y=y2;
141     }
142     if(l21==maxl)
143     {
144         t[a].x=y1;
145         t[a].y=x2;
146     }
147     if(l22==maxl)
148     {
149         t[a].x=y1;
150         t[a].y=y2;
151     }
152     if(t[a].r==maxl)
153     {
154         t[a].x=x1;
155         t[a].y=y1;
156     }
157     if(t[fb].r==maxl)
158     {
159         t[a].x=x2;
160         t[a].y=y2;
161     }
162     t[a].r=maxl;
163     f[fb]=a;
164 }
165 void put(int p)
166 {
167     t[p]=tree{0,p,p};
168     f[p]=p;
169     for(int i=0;i<(int)g[p].size();i++)
170     {
171         int to=g[p][i].to;
172         if(f[to]==-1)
173             continue;
174         merg(p,to);
175     }
176     ans=max(ans,d[p]*t[p].r);
177 }
178 int main()
179 {
180     freopen("in.txt","r",stdin);
181     int T;
182     scanf("%d",&T);
183     while(T--)
184     {
185         memset(ch,0,sizeof(ch));
186         memset(fa,0,sizeof(fa));
187         memset(len,0,sizeof(len));
188         memset(deep,0,sizeof(deep));
189         //scanf("%I64d",&n);
190         scanf("%lld",&n);
191         for(int i=0;i<=n;i++)
192             g[i].clear();
193         for(int i=1;i<=n;i++)
194         {
195             scanf("%lld",d+i);
196             a[i-1]=i;
197         }
198         int x,y;
199         long long v;
200         for(int i=1;i<n;i++)
201         {
202             //scanf("%d%d%I64d",&x,&y,&v);
203             scanf("%d%d%lld",&x,&y,&v);
204             g[x].push_back(node{y,v});
205             g[y].push_back(node{x,v});
206         }
207         dfs();
208         initFa(n);
209         sort(a,a+n,cmp);
210         memset(f,-1,sizeof(f));
211         memset(t,0,sizeof(t));
212         ans=-1;
213         for(int i=0;i<n;i++)
214         {
215             put(a[i]);
216         }
217         //printf("%I64d
",ans);
218         printf("%lld
",ans);
219     }
220     return 0;
221 }
View Code

 思路2:对点考虑, 对于当前点s(每次选取重心), 可以求出以它为根, 每一个子树上的点上到s的点权最小值, 和边权和, 然后 把每一个每一个子树分类, 这是为了避免计算的结果在同一个子树里。

然后计算不同子树中的结果, 这里计算结果时需要排个序, 按照点权最小值从大到小排序, 每次选取两个属于不同子树的最大边权和。

如此递归计算即可

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int MAXN = 1e5+10;
  4 int siz[MAXN], n, val[MAXN];
  5 bool center[MAXN];
  6 
  7 typedef pair<int, int>pii;
  8 struct Edge{
  9     int to, next;
 10     int c;
 11 }e[MAXN * 2];
 12 int head[MAXN], edge_tot;
 13 void Add_Edge(int x, int y, int z){
 14     e[edge_tot].to = y;
 15     e[edge_tot].next = head[x];
 16     e[edge_tot].c = z;
 17     head[x] = edge_tot++;
 18 }
 19 
 20 void init (){
 21     edge_tot = 0;
 22     memset(head, -1, sizeof (head));
 23     memset(center, 0, sizeof (center));
 24 }
 25 
 26 pair <pair<long long, long long>, int>Mval[MAXN];
 27 pii Find(int s, int pa, int tot) {
 28     pii res = make_pair(INT_MAX, -1);
 29     int m = 0;
 30     siz[s] = 1;
 31     for (int i = head[s]; ~i; i = e[i].next) {
 32         int  u = e[i].to;
 33         if (u == pa || center[u]) {
 34             continue;
 35         }
 36         res = min(res, Find(u, s, tot));
 37         siz[s] += siz[u];
 38         m = max(m, siz[u]);
 39     }
 40     m = max(m, tot-siz[s]);
 41     return min(res, make_pair(m, s));
 42 }
 43 
 44 int idx, tim;
 45 void Get_Min_Sum(int u, int pa, long long minval, long long sum){
 46     Mval[idx++] = make_pair(make_pair(minval, sum), tim);
 47     for (int i = head[u]; ~i; i = e[i].next){
 48         int v = e[i].to;
 49         if (v != pa && !center[v]){
 50             Get_Min_Sum(v, u, min(minval, (long long)val[v]), sum+e[i].c);
 51         }
 52     }
 53 }
 54 long long sub_solve(){
 55     sort (Mval, Mval+idx);
 56     long long res = 0;
 57     long long sum1 = Mval[idx-1].first.second, sum2 = 0;
 58     int t1 = Mval[idx-1].second;
 59     for (int i = idx-2; i >= 0; i--){
 60         if (Mval[i].second != t1){
 61             res = max(res, Mval[i].first.first*(Mval[i].first.second+sum1));
 62         }else{
 63             res = max(res, Mval[i].first.first*(Mval[i].first.second+sum2));
 64         }
 65         long long tmp = Mval[i].first.second;
 66         if (tmp > sum1){
 67             if (Mval[i].second == t1){
 68                 sum1 = tmp;
 69             }else{
 70                 sum2 = sum1;
 71                 //t2 = t1;
 72                 sum1 = tmp;
 73                 t1 = Mval[i].second;
 74             }
 75 
 76         }else{
 77             if (tmp > sum2 && Mval[i].second != t1){
 78                 sum2 = tmp;
 79                 //t2 = Mval[i].second;
 80             }
 81         }
 82 
 83     }
 84     return res;
 85 }
 86 long long solve (int u, int tot){
 87     int g = Find(u, 0, tot).second;
 88     center[g] = true;
 89     long long res = 0;
 90     idx = 0;
 91     //tim++;
 92     Mval[idx++] = make_pair(make_pair(val[g],0), tim);
 93     for (int i = head[g]; ~i; i = e[i].next){
 94         int v = e[i].to;
 95         int cost = e[i].c;
 96         if (!center[v]){
 97             tim++;
 98             Get_Min_Sum(v, g, min(val[v], val[g]), cost);
 99         }
100     }
101     res = max(res, sub_solve());
102     for (int i = head[g]; ~i; i = e[i].next){
103         int v = e[i].to;
104         if (!center[v]){
105             res = max(res, solve(v, siz[v]));
106         }
107     }
108     return res;
109 }
110 
111 int main()
112 {
113     //freopen("in.txt", "r", stdin);
114     int T;
115     scanf ("%d", &T);
116     while (T--){
117         init();
118         tim = 0;
119         scanf ("%d", &n);
120         for (int i = 0; i < n; i++){
121             scanf ("%d", val+i+1);
122         }
123         for (int i = 0; i < n-1; i++){
124             int u, v, c;
125             scanf ("%d%d%d", &u, &v, &c);
126             Add_Edge(u, v, c);
127             Add_Edge(v, u, c);
128         }
129         long long res = solve(1, n);
130         printf("%lld
", res);
131 
132     }
133     return 0;
134 }
View Code
原文地址:https://www.cnblogs.com/oneshot/p/4682287.html