[51nod] 1766 树上的最远点对

Posted Lev今天学习了吗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[51nod] 1766 树上的最远点对相关的知识,希望对你有一定的参考价值。

1766 树上的最远点对 

基准时间限制:3 秒 空间限制:524288 KB 分值: 80  难度:5级算法题

 
 
n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j<=d}
(PS 建议使用读入优化)
 
Input
第一行一个数字 n n<=100000。
第二行到第n行每行三个数字描述路的情况, x,y,z (1<=x,y<=n,1<=z<=10000)表示x和y之间有一条长度为z的路。
第n+1行一个数字m,表示询问次数 m<=100000。
接下来m行,每行四个数a,b,c,d。
Output
共m行,表示每次询问的最远距离
Input示例
5
1 2 1
2 3 2
1 4 3
4 5 4
1
2 3 4 5
Output示例
10
Analysis
洛谷秋令营讲解过的题目,因此并没有用很多时间进行深刻的思考
基本构架:线段树,用于处理区间询问
首先我们有A B两个点集,从A B中各取一个点以组成最远点对,求出其距离
首先可以有这一个结论:最后的最远点对的点,在A中的,必然也是A中最远点对的其中一个点
因此线段树维护每个区间的最远点对,当区间合并的时候取他们各自的最远点对暴力取优即可
(百度一发题解,有人指出最远点对的合并与树的直径的合并具有相似性,因此得出上面的结论)
(但是本蒟蒻很迷啊)
好,接下来谈实现
1. 线段树!
首先线段树基本框架没有问题,我们需要实现一个函数:合并(merge)
这里推荐在单点区间内把最远点对的两个点都设成自己(第一个坑)
不然合并的时候爆炸(我将不存在的点设成了-1结果qwq)
最后的答案要求从A B各取一个点,所以合并函数还需要注意:线段树内的合并可以从单个集合中取点对,而最终答案不行
2. 求距离!
要求求出最近公共祖先(然后瞎搞)
倍增LCA,TLE最后6个点
虽然我没怎么卡常数而且写的非常粗糙(比如没有记忆化每次都要重新求)
但是显然这是算法问题
所以找CCZ求得了一个O(1)LCA的方案
orz ccz!
首先求出当前树的欧拉序(不同于DFS序的是,这里的欧拉序要求包括每一次进入的结点)

然后对于这份DFS序,我们要做的就是:求出区间(以U V的随便哪个DFN值做区间端点)的最小值(这里的区间保存的是深度Depth)所对应的那个点
显然,那个点就是U V的LCA
而使用ST表的话可以做到O(1)查询
qwq 然而我没学ST表啊 qwq
所以最后跑去看了qt666的博客
qwq (其实代码中的ST表代码是无缝移植qt666的,表示感谢!)
(找个时间还是要重写一遍的,一是不大好无缝移植,趁早学会重造比较好,二是各种调试代码太多)
(敲这道题的时候我居然没有重写)
Code
  1 #include<stdio.h>
  2 #include<iostream>
  3 #define maxn 1000000
  4 #define lc (rt<<1)
  5 #define rc (rt<<1|1)
  6 #define mid ((L+R)/2)
  7 using namespace std;
  8 
  9 int dfn[maxn],dfnn[maxn],TIM,line[maxn],chain[maxn],sum[maxn];
 10 int pre[maxn],pre2[maxn],ST[maxn][30];
 11 int fa[maxn][20],ro[maxn][20],depth[maxn],n,m;
 12 
 13 struct edge{ int from,u,v,len; }e[maxn];
 14 int tot,first[maxn];
 15 void insert(int u,int v,int len){ tot++; e[tot].from = first[u], e[tot].u = u , e[tot].v = v , e[tot].len = len, first[u] = tot; }
 16 
 17 void dfs(int now){
 18     dfn[now] = ++TIM;
 19     line[TIM] = -depth[now];
 20     chain[TIM] = now;
 21     for(int i = first[now];i;i = e[i].from){
 22         int v = e[i].v;
 23         if(v != fa[now][0]){
 24             sum[v] = sum[now]+e[i].len;
 25             depth[v] = depth[now]+1;
 26             fa[v][0] = now;
 27 //            ro[v][0] = e[i].len;
 28             dfs(v);
 29             line[++TIM] = -depth[now];
 30             chain[TIM] = now;
 31         }
 32     }
 33 }
 34 void Init(){
 35     for(int i = 1;i <= 18;i++)
 36     for(int j = 1;j <= n;j++)
 37         fa[j][i] = fa[fa[j][i-1]][i-1],
 38         ro[j][i] = ro[fa[j][i-1]][i-1]+ro[j][i-1];
 39 }
 40 
 41 void makeST()
 42 {
 43   pre[0]=1;for(int i=1;i<=20;i++) pre[i]=pre[i-1]<<1;
 44   pre2[0]=-1;for(int i=1;i<=TIM;i++) pre2[i]=pre2[i>>1]+1;
 45   for(int i=1;i<=TIM;i++) ST[i][0]=i;
 46   for(int j=1;j<=20;j++)
 47     for(int i=1;i<=TIM;i++){
 48       if(i+pre[j]-1<=TIM){
 49        int x1=ST[i][j-1],x2=ST[i+pre[j-1]][j-1];
 50        if(line[x1]>line[x2]) ST[i][j]=x1;
 51        else ST[i][j]=x2;
 52       }
 53     }
 54 }int queryST(int l,int r)
 55 {
 56     l = dfn[l],r = dfn[r];
 57     if(l > r) swap(l,r);
 58 //    printf("query:[%d , %d]\\n",l,r);
 59   if(l==r)return chain[l];
 60   int x=pre2[r-l+1];
 61   int x1=ST[l][x],x2=ST[r-pre[x]+1][x];
 62   return line[x1]>line[x2]?chain[x1]:chain[x2];
 63 }
 64 
 65 int getdis(int u,int v){
 66     if(depth[u] < depth[v]) swap(u,v);
 67     int anc = queryST(u,v);
 68     if(anc == v) return sum[u]-sum[anc];
 69     else return sum[u]+sum[v]-sum[anc]*2;
 70     /*int ret = 0;
 71     if(depth[u] < depth[v]) swap(u,v);
 72     for(int i = 18;i >= 0;i--)
 73         if(depth[fa[u][i]] >= depth[v])
 74             ret += ro[u][i],
 75             u = fa[u][i];
 76     if(u == v) return ret;
 77     for(int i = 18;i >= 0;i--)
 78         if(fa[u][i] != fa[v][i])
 79             ret += ro[u][i]+ro[v][i],
 80             u = fa[u][i],v = fa[v][i];
 81     return ret + ro[u][0] + ro[v][0];*/
 82 }
 83 struct node{ int A,B; node(){A = B = -1;}}Tree[maxn*4];
 84 node merge(node a,node b,int mode){
 85     node tmp;
 86 //    printf("What I get: a.A%d a.B%d b.A%d b.B%d\\n",a.A,a.B,b.A,b.B);
 87     int dis = -1;
 88     if(a.A > 0 && a.B > 0 && mode){
 89         int cnt = getdis(a.A,a.B);
 90         if(cnt > dis) dis = cnt,tmp.A = a.A,tmp.B = a.B;
 91     }if(a.A > 0 && b.A > 0){
 92         int cnt = getdis(a.A,b.A);
 93         if(cnt > dis) dis = cnt,tmp.A = a.A,tmp.B = b.A;
 94     }if(a.A > 0 && b.B > 0){
 95         int cnt = getdis(a.A,b.B);
 96         if(cnt > dis) dis = cnt,tmp.A = a.A,tmp.B = b.B;
 97     }if(a.B > 0 && b.A > 0){
 98         int cnt = getdis(a.B,b.A);
 99         if(cnt > dis) dis = cnt,tmp.A = a.B,tmp.B = b.A;
100     }if(a.B > 0 && b.B > 0){
101         int cnt = getdis(a.B,b.B);
102         if(cnt > dis) dis = cnt,tmp.A = a.B,tmp.B = b.B;
103     }if(b.A > 0 && b.B > 0 && mode){
104         int cnt = getdis(b.A,b.B);
105         if(cnt > dis) dis = cnt,tmp.A = b.A,tmp.B = b.B;
106 //        cout << "Who visit me qwq?" << endl;
107     }
108 //    cout << "dis: " << dis << endl;
109     
110     return tmp;
111 }
112 
113 void build(int rt,int L,int R){
114     if(L == R) Tree[rt].A = L,Tree[rt].B = L;
115     else{
116         build(lc,L,mid);
117         build(rc,mid+1,R);
118         
119         Tree[rt] = merge(Tree[lc],Tree[rc],1);
120     }
121 //    printf("#%d: L%d R%d A%d B%d\\n",rt,L,R,Tree[rt].A,Tree[rt].B);
122 }
123 
124 node query(int rt,int L,int R,int qL,int qR){
125     if(qL <= L && R <= qR) return Tree[rt];
126     else{
127         node tmp;
128         if(qL <= mid) tmp = merge(tmp,query(lc,L,mid,qL,qR),1);
129         if(qR > mid) tmp = merge(tmp,query(rc,mid+1,R,qL,qR),1);
130 //        printf("tmp now is: %d %d\\n",tmp.A,tmp.B);
131         return tmp;
132     }
133 }
134 
135 int read(){
136     int ret = 0; char ctr = getchar();
137     while(ctr < \'0\' || ctr > \'9\') ctr = getchar();
138     while(ctr >= \'0\' && ctr <= \'9\') ret = ret*10+ctr-\'0\',ctr = getchar();
139     return ret;
140 }
141 
142 int main(){
143 //    scanf("%d",&n);
144     n = read();
145     for(int i = 1;i < n;i++){
146         int x,y,z; //scanf("%d%d%d",&x,&y,&z);
147         x = read(),y = read(),z = read();
148         insert(x,y,z); insert(y,x,z);
149     }depth[1] = 1; dfs(1); Init();
150     makeST();
151 //    printf("dis(1,3) %d\\n",getdis(1,3));
152 //    scanf("%d",&m);
153     m = read();
154 //    for(int i = 0;i <= TIM;i++){
155 //    for(int j = 0;j <= 9;j++)
156 ////        printf("%d ",ST[i][j]);
157 //        cout << endl;
158 //    }
159 //    for(int i = 0;i <= TIM;i++) printf("%d ",line[i]); cout << endl;
160 //    for(int i = 0;i <= TIM;i++) printf("%d ",chain[i]);
161 //    printf("LCA %d dis(3,4) = %d sum3 %d sum4 %d\\n",queryST(3,4),getdis(3,4),sum[3],sum[4]);
162 //    printf("LCA of :%d %d = %d\\n",1,4,query(1,4));
163     build(1,1,n);
164     while(m--){
165         int a,b,c,d;
166 //        scanf("%d%d%d%d",&a,&b,&c,&d);
167         a = read(),b = read(),c = read(),d = read();
168 //        cout << "AAA";
169         node ans = merge(query(1,1,n,a,b),query(1,1,n,c,d),0);
170 //        cout << "AAA";
171 //        cout << "Great" << ans.A << ans.B << endl;
172         printf("%d\\n",getdis(ans.A,ans.B));
173     }
174     
175 //    for(int i = 0;i <= 20;i++)
176 //        printf("#%d: %d %d\\n",i,Tree[i].A,Tree[i].B);    
177     return 0;
178 }
qwq

以上是关于[51nod] 1766 树上的最远点对的主要内容,如果未能解决你的问题,请参考以下文章

51nod1766 树上的最远点对

51 nod 1766 树上的最远点对(线段树+lca)

51nod 1766 树上的最远点对(线段树)

51nod 1766 树上的最远点对

做题51Nod1766树上的最远点对——直径&线段树

51Nod.1766.树上最远点对(树的直径 RMQ 线段树/ST表)