计蒜客 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\n",ans); 218 printf("%lld\n",ans); 219 } 220 return 0; 221 }
思路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\n", res); 131 132 } 133 return 0; 134 }