Day 5 LCA 最近公共祖先
文章目錄
A. POJ 1330 Nearest Common Ancestors
題目
題解
LCA倍增求法模板題。唯一噁心的地方就是輸入的樹不一定是以1號節點為根節點的,需要找到根節點,至於方法,我相信你會。。。。。。我一直WA的原因:lca中的判斷步驟少寫了一個。
程式碼
#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1e4+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch)) ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn],Next[maxn],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
queue<int>q;
int d[maxn],f[maxn][21],vis[maxn],t;
inline void bfs(int root)
{
q.push(root);
d[root]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (d[y]) continue;
d[y]=d[x]+1;
f[y][0]=x;
for (int j=1;j<=t;++j)
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
inline int lca(int x,int y)
{
if (d[x]>d[y]) swap(x,y);
for (int i=t;i>=0;--i)
if (d[f[y][i]]>=d[x]) y=f[y][i];//==就是這裡!!!!!!!==
if (x==y) return x;
for (int i=t;i>=0;--i)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
int c;read(c);
while (c--)
{
int n;read(n);
t=(int)(log(n)/log(2))+1;
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
len=0;
for (int i=1;i<n;++i)
{
int x,y;read(x);read(y);
add(x,y);
vis[y]=1;
}
for (int i=1;i<=n;++i)
if (!vis[i])
{
bfs(i);
break;
}
int x,y;
read(x);read(y);
printf("%d\n",lca(x,y));
}
return 0;
}
B. HDU 2586 How far away ?
題目
題解
不會的,通通回去讀《演算法競賽進階指南》。
程式碼
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int f=1,num=0;
char ch=getchar();
while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
while (ch>='0'&&ch<='9') num=(num<<1)+(num<<3)+ch-'0', ch=getchar();
return num*f;
}
const int SIZE=50010;
int f[SIZE][20],d[SIZE],dist[SIZE];
int ver[SIZE*2],Next[SIZE*2],edge[SIZE*2],head[SIZE];
int T,n,m,tot,t;
queue<int>q;
void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void bfs()
{//預處理
q.push(1);
d[1]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (d[y]) continue;
d[y]=d[x]+1;
dist[y]=dist[x]+edge[i];
f[y][0]=y;
for (int j=1;j<=t;j++)
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
int lca(int x,int y)//回答一個詢問
{
if (d[x]>d[y]) swap(x,y);
for (int i=t;i>=0;i--)
if (d[f[y][i]]>=d[x]) y=f[y][i];
if (x==y) return x;
for (int i=t;i>=0;i--)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
T=read();
while (T--)
{
n=read(),m=read();
t=(int)(log(n)/log(2))+1;
//清空
for (int i=1;i<=n;++i)
head[i]=d[i]=0;
tot=0;
//讀入一棵樹
for (int i=1;i<n;++i)
{
int x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
bfs();
//回答問題
for (int i=1;i<=m;++i)
{
int x=read(),y=read();
printf("%d\n",dist[x]+dist[y]-2*dist[lca(x,y)]);
}
}
return 0;
}
C. BZOJ 1787: [Ahoi2008]Meet 緊急集合
題目
題解
求三個結點到一個結點距離之和最小的結點以及距離和
求出兩兩lca,其中有兩個相同,答案則為另一個。
摘自hzwer
程式碼
來自Tyouchie的程式碼。
#include<bits/stdc++.h>
#define N 500001
using namespace std;
int f[N][20],d[N],dist[N],lin[N*2],ans;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
return s * w;
}
struct gg
{
int y,v,next;
}a[N<<1];
int T,n,m,tot,t;
queue<int> q;
void add(int x,int y)
{
a[++tot].y=y;
a[tot].next=lin[x];
lin[x]=tot;
}
void bfs()
{
q.push(1);d[1]=1;
while(q.size())
{
int x=q.front();q.pop();
for(int i=lin[x];i;i=a[i].next)
{
int y=a[i].y;
if(d[y]) continue;
d[y]=d[x]+1;
f[y][0]=x;
for(int j=1;j<=t;j++)
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
int lca(int x,int y)
{
ans=0;
if(d[x]>d[y]) swap(x,y);
for(int i=t;i>=0;i--)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=t;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return ans=f[x][0];
}
int dis(int x,int y)
{
int t=lca(x,y);
return d[x]+d[y]-2*d[t];
}
int main()
{
queue<int> q;
n=read();m=read();
t=(int)(log(n)/log(2))+1;
for(int i=1;i<=n;i++) lin[i]=d[i]=0;
tot=0;
for(int i=1;i<n;i++)
{
int x,y;
x=read();y=read();
add(x,y);add(y,x);
}
bfs();
for(int i=1;i<=m;i++)
{
int x,y,z;
x=read();y=read();z=read();
int xx,yy,zz;
int p1=lca(x,y),p2=lca(x,z),p3=lca(y,z),t;
if(p1==p2) t=p3;
else if(p2==p3) t=p1;
else t=p2;
int ans=dis(x,t)+dis(y,t)+dis(z,t);
cout<<t<<' '<<ans<<endl;
}
return 0;
}
或者將三個lca分別計算取最優。
下面這份程式碼跑的較快。大概左右。上份程式碼大概是左右。
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int inf=0x7fffffff;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch)) ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
queue<int>q;
int d[maxn],f[maxn][21],vis[maxn],t;
inline void bfs(int root)
{
q.push(root);
d[root]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (d[y]) continue;
d[y]=d[x]+1;
f[y][0]=x;
for (int j=1;j<=t;++j)
f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
inline int lca(int x,int y)
{
if (d[x]>d[y]) swap(x,y);
for (int i=t;i>=0;--i)
if (d[f[y][i]]>=d[x]) y=f[y][i];
if (x==y) return x;
for (int i=t;i>=0;--i)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main()
{
int n,m,id,ans;read(n);read(m);
t=log2(n*1.0);
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
len=0;
for (int i=1;i<n;++i)
{
int x,y;read(x);read(y);
add(x,y);add(y,x);
}
bfs(1);
for (int i=1;i<=m;++i)
{
int x,y,z;read(x);read(y);read(z);
int l1=lca(x,y),l2=lca(x,z),l3=lca(y,z),ans=inf,tmp,id;
int q1=lca(l1,z),q2=lca(l2,y),q3=lca(l3,x);
tmp=d[x]+d[y]-d[l1]+d[z]-(d[q1]<<1);
if (tmp<ans)
ans=tmp,id=l1;
tmp=d[x]+d[z]-d[l2]+d[y]-(d[q2]<<1);
if (tmp<ans)
ans=tmp,id=l2;
tmp=d[y]+d[z]-d[l3]+d[x]-(d[q3]<<1);
if (tmp<ans)
ans=tmp,id=l3;
printf("%d %d\n",id,ans);
}
return 0;
}
D. UVA11354 Bond
題目
LUOGU UVA 11354 最小生成樹+倍增求LCA
雙倍經驗:NOIP 2013 貨車運輸 最大生成樹+倍增求LCA
題解
這個題啊!真噁心啊!
1.首先,我們將讀入資料用最小生成樹直接建立一顆樹。
2.然後,再跑一遍倍增求LCA,既可以了。
細節等空閒時在說清楚。其實程式碼很清楚的
程式碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5+10;
const int inf=0x3f3f3f3f;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
struct Edge
{
int x,y,z,next;
}G[maxn<<1],A[maxn<<1];//G[]是最大生成樹的圖
int n,m,head[maxn],len;
inline void add(int x,int y,int z)
{
G[++len].y=y,G[len].z=z,G[len].next=head[x],head[x]=len;
}
int fa[maxn];
inline int get(int x)
{
if (x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
inline bool cmp(Edge a,Edge b)
{
// return a.z>b.z;
return a.z<b.z;
}
inline void Kruskal()
{
sort(A+1,A+m+1,cmp);
for (int i=1;i<=n;++i)
fa[i]=i;
for (int i=1;i<=m;++i)
{
int x=get(A[i].x),y=get(A[i].y);
if (x!=y)
{
fa[y]=x;
add(A[i].x,A[i].y,A[i].z);
add(A[i].y,A[i].x,A[i].z);
}
}
}
int d[maxn],f[maxn][21],w[maxn][21];//fa[]表示並查集中的父節點,f[][]表示樹上的父節點,w[][]表示最大載重
inline void dfs(int x)
{
for (int i=1;i<=20;++i)//LCA初始化
{
f[x][i]=f[f[x][i-1]][i-1];
// w[x][i]=min(w[x][i-1],w[f[x][i-1]][i-1]);
w[x][i]=max(w[x][i-1],w[f[x][i-1]][i-1]);
}
for (int i=head[x];i;i=G[i].next)
{
int y=G[i].y;
if (d[y]) continue;
d[y]=d[x]+1;//計算深度
f[y][0]=x;//儲存父節點
w[y][0]=G[i].z;//儲存到父節點的權值
dfs(y);
}
}
inline int lca(int x, int y)
{
if (get(x)!=get(y)) return -1;//不連通,輸出-1
// int ans=inf;
int ans=0;
if (d[x]>d[y]) swap(x,y);//保證y節點更深
for (int i=20;i>=0;--i)//將y節點上提到於x節點相同深度
if (d[f[y][i]]>=d[x])
{
// ans=min(ans,w[y][i]);//更新最大載重(最小邊權)
ans=max(ans,w[y][i]);
y=f[y][i];//修改y位置
}
if (x==y) return ans;//如果位置已經相等,直接返回答案
for (int i=20;i>=0;--i)//尋找公共祖先
if (f[x][i]!=f[y][i])
{
// ans=min(ans,min(w[x][i], w[y][i]));//更新最大載重(最小邊權)
ans=max(ans,max(w[x][i],w[y][i]));
x=f[x][i],y=f[y][i];//修改x,y位置
}
// ans=min(ans,min(w[x][0],w[y][0]));//更新此時x,y到公共祖先最大載重,f[x][0], f[y][0]即為公共祖先
ans=max(ans,max(w[x][0],w[y][0]));
return ans;
}
int main()
{
int flag=0;
while (scanf("%d %d",&n,&m)!=EOF)
{
if (flag) printf("\n");
else flag=1;
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
memset(d,0,sizeof(d));
memset(head,0,sizeof(head));
len=0;
for (int i=1;i<=m;++i)
read(A[i].x),read(A[i].y),read(A[i].z);
Kruskal();
for (int i=1;i<=n;++i)//dfs收集資訊
if (!d[i])
{
d[i]=1;
dfs(i);
f[i][0]=i;
// w[i][0]=inf;
w[i][0]=-inf;
}
int q;
read(q);
while (q--)
{
int x,y;
read(x);read(y);
printf("%d\n",lca(x,y));
}
}
return 0;
}
E. BZOJ 1977 【模板】嚴格次小生成樹[BJWC2010]
題目
程式碼
之前寫過這個題,今天再回顧一遍吧,但是部落格不想再贅述了,就看這個吧。
嚴格次小生成樹
F. BZOJ 2144: 跳跳棋
題目
題解
真的不會,這個是抄黃學長的程式碼。 QWQ
程式碼
/**************************************************************
Problem: 2144
User: hsm
Language: C++
Result: Accepted
Time:20 ms
Memory:1292 kb
****************************************************************/
#include<bits/stdc++.h>
#define inf 1000000000
using namespace std;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int tmp,ans;
int a[5],b[5];
struct data
{
int a[5];
};
data cal(int *a,int k)//得到a狀態向上走k次的狀態
{
data ans;
int t1=a[2]-a[1],t2=a[3]-a[2];
for (int i=1;i<=3;++i)
ans.a[i]=a[i];
if (t1==t2) return ans;
if (t1<t2)
{
int t=min(k,(t2-1)/t1);
k-=t,tmp+=t;//順便記錄深度
ans.a[2]+=t*t1,ans.a[1]+=t*t1;
}
else
{
int t=min(k,(t1-1)/t2);
k-=t,tmp+=t;
ans.a[2]-=t*t2,ans.a[3]-=t*t2;
}
if (k) return cal(ans.a,k);//輾轉相除
else return ans;
}
bool operator != (data a,data b)
{
for (int i=1;i<=3;++i)
if (a.a[i]!=b.a[i])
return 1;
return 0;
}
int main()
{
for (int i=1;i<=3;++i)
read(a[i]);
for (int i=1;i<=3;++i)
read(b[i]);
sort(a+1,a+4);
sort(b+1,b+4);
data t1=cal(a,inf);int d1=tmp;tmp=0;
data t2=cal(b,inf);int d2=tmp;tmp=0;//t1,t2分別為a,b的根,d1,d2為深度
if (t1!=t2)
{
puts("NO");
return 0;
}
if (d1>d2)
{
swap(d1,d2);
for (int i=1;i<=3;++i)
swap(a[i],b[i]);
}
ans=d2-d1;
t1=cal(b,ans);
for (int i=1;i<=3;++i)
b[i]=t1.a[i]; //較深的向上調整
int l=0,r=d1;
while (l<=r)//二分
{
int mid=(l+r)>>1;
if (cal(a,mid)!=cal(b,mid)) l=mid+1;
else r=mid-1;
}
puts("YES");
printf("%d",ans+2*l);
return 0;
}
G. BZOJ 2815:[ZJOI2012]災難
題目
題解
題中所帶的圖真的很讓人無語。我嘗試用想到的演算法去推這個圖,都以失敗告終。這個圖的唯一作用估計就是誤導人了。
剛開始的時候,我嘗試著求出兩個點的,然而發現這個好像是個有向圖,不太會寫,應該是要再建圖的吧。後來看了洛谷的演算法標籤,就想到了可以用先處理出這些點的依賴關係,但是之後該怎麼寫就不懂了,但是好歹寫了這麼長時間程式碼,所以能夠想到最後一定是要重新建圖,然後統計答案。
之後就各種亂搞,結果掛掉了。最後還是忍不住看了題解,發現題解真是百花齊放,各種神奇操作的都有,例如用什麼之類的,反正我是不會寫的啦。找了一個能看懂的,看了半個小時,開始寫了,發現原來有些難題就是把各種板子強行堆在一起。。。。。。然後開開心心的過了樣例,提交,20分。
(⊙o⊙)…,有點蒙。然後開啟題解照著對,發現統計答案的時候,題解上寫的是從0開始的,而我寫的是1。難不成就這麼小的差別?改了一下,結果。(⊙o⊙)…。。。。。。。。。。。。我現在能想到解釋就是可能我那樣寫會導致根節點的答案會被漏算。如果哪位大佬知道為什麼,請一定要通知我!
好了,正式說題解。
1.為了服務於後面的操作,我們需要另外建一張圖(原圖的反圖),來利用搭建一個方便我們統計答案的圖。
2.因為這道題目說白了就是求每個節點可以影響幾個節點的狀態,這就很容易的想到了,開一個陣列搞出來一組拓撲序。
3.之後,就是在反圖上,按照拓撲序一次將每個節點和他的連邊的節點的,連一條到的的邊,作為新建的圖中的一條邊。
4.之後,就是跑一邊統計答案了。注意一點,因為我們在遞迴的過程中給每個節點的答案都多加了1,所以輸出時減去1即可。
哦,對了,還有個問題:鄰接表陣列要開4倍,我也不知道為什麼,反正開兩倍會RE,這是實踐告訴我的。。。。。
程式碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=65540;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn<<2],Next[maxn<<2],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int ver1[maxn<<2],Next1[maxn<<2],head1[maxn],len1;
inline void add1(int x,int y)
{
ver1[++len1]=y,Next1[len1]=head1[x],head1[x]=len1;
}
int ver2[maxn<<2],Next2[maxn<<2],head2[maxn],len2;
inline void add2(int x,int y)
{
ver2[++len2]=y,Next2[len2]=head2[x],head2[x]=len2;
}
int ran[maxn],In[maxn],n;
inline void topsort()
{
queue<int>q;
for (int i=1;i<=n;++i)
if (!In[i]) q.push(i);
int num=0;
while (!q.empty())
{
int x=q.front();
q.pop();
ran[++num]=x;
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (!--In[y]) q.push(y);
}
}
}
int f[maxn][21],d[maxn];
inline int lca(int x,int y)
{
if (d[x]>d[y]) swap(x,y);
for (int i=20;i>=0;--i)
if (d[f[y][i]]>=d[x])
y=f[y][i];
if (x==y) return x;
for (int i=20;i>=0;--i)
if (f[x][i]^f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int ans[maxn];
inline void dfs(int x)
{
for (int i=head2[x];i;i=Next2[i])
{
int y=ver2[i];
dfs(y);
ans[x]+=ans[y];
}
++ans[x];
}
int main()
{
read(n);
for (int i=1;i<=n;++i)
{
int x;read(x);
while (x)
{
++In[i],add1(i,x);
add(x,i);
read(x);
}
}
topsort();
for (int i=1;i<=n;++i)
{
int u=ran[i],x=ver1[head1[u]];
for (int k=head1[u];k;k=Next1[k])
x=lca(x,ver1[k]);
add2(x,u);
d[u]=d[x]+1;
f[u][0]=x;
for (int k=1;k<=20;++k)
f[u][k]=f[f[u][k-1]][k-1];
}
dfs(0);
for (int i=1;i<=n;++i)
printf("%d\n",ans[i]-1);
return 0;
}
H. BZOJ 4326: NOIP2015 運輸計劃
題目
題解
思路: 最大值最小,顯然可以二分:二分答案,
然後check的時候把所有大於mid值的路徑記錄下來,找出被所有這樣路徑覆蓋的最長的道路:
如果沒有這樣的道路,返回false;
如果這樣的道路被減去之後依然大於mid,返回false
找出被所有路徑覆蓋的道路:在樹中將所有路徑起、始權值+1,LCA權值-2,從所有葉節點往上累加(dfs序維護真是好),最終權值為路徑數的點到其父親的邊為所求邊 dis[i]表示i到根的距離;
tmp[i]表示i這個點通往父親的邊,目的是記錄這條邊被遍歷的次數 ;
num[i]的作用是找到葉節點向上累加。
——D_14134
程式碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
template<typename T>inline void read(T &x)
{
x=0;
T f=1,ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
struct Road
{
int fr,to,lca,di;
}r[maxn<<1];
int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y,int z)
{
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
bool vis[maxn];
int num[maxn],top;
int d[maxn],dist[maxn],f[maxn][21];
inline void dfs(int x,int fa,int deep)
{
num[++top]=x,d[x]=deep,vis[x]=1;
for (int i=1;i<=20;++i)
f[x][i]=f[f[x][i-1]][i-1];
for (int i=head[x];i;i=Next[i])
{
int y=ver[i];
if (!vis[y])
{
f[y][0]=x;
dist[y]=dist[x]+edge[i];
dfs(y,x,deep+1);
}
}
}
inline int LCA(int x,int y)
{
if (d[x]>d[y]) swap(x,y);
for (int i=20;i>=0;--i)
if (d[f[y][i]]>=d[x]) y=f[y][i];
if (x==y) return x;
for (int i=20;i>=0;--i)
if (f[x][i]^f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int temp[maxn],n,m;
inline bool check(int mid)
{
int cnt=0,ans=0;
memset(temp,0,sizeof(temp));
for (int i=1;i<=m;++i)
if (r[i].di>mid)
{
++temp[r[i].fr],++temp[r[i].to],temp[r[i].lca]-=2;
ans=max(ans,r[i].di-mid);
++cnt;
}
if (!cnt) return true;
for (int i=n;i>=1;--i)
temp[f[num[i]][0]]+=temp[num[i]];
for (int i=2;i<=n;++i)
if (temp[i]==cnt && dist[i]-dist[f[i][0]]>=ans)
return true;
return false;
}
int main()
{
int sum=0;
read(n);read(m);
for (int i=1;i<n;++i)
{
int x,y,z;
read(x);read(y);read(z);
add(x,y,z);add(y,x,z);
sum+=z;
}
dist[1]=0;
dfs(1,0,1);
for (int i=1;i<=m;++i)
{
read(r[i].fr);read(r[i].to);
r[i].lca=LCA(r[i].fr,r[i].to);
r[i].di=dist[r[i].fr]+dist[r[i].to]-(dist[r[i].lca]<<1);
}
int l=0,r=sum;
while (l<r)
{
int mid=(l+r)>>1;
if (check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
return 0;
}
相關文章
- LCA最近公共祖先
- 最近公共祖先 LCA
- 演算法學習筆記(5): 最近公共祖先(LCA)演算法筆記
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- 最近公共祖先
- 樹上公共祖先(LCA)
- 樹上問題/簡單演算法 LCA【最近公共祖先】演算法
- LeetCode——最近公共祖先LeetCode
- Google S2 中的四叉樹求 LCA 最近公共祖先Go
- 樹的最近公共祖先問題
- 二叉樹的最近公共祖先二叉樹
- 「學習筆記」tarjan 求最近公共祖先筆記
- Git 中的演算法-最近公共祖先Git演算法
- 0235-二叉搜尋樹的最近公共祖先
- leetcode 235. 二叉搜尋樹的最近公共祖先LeetCode
- (117)235. 二叉搜尋樹的最近公共祖先(leetcode)LeetCode
- # 劍指 Offer 68 - II. 二叉樹的最近公共祖先二叉樹
- 二叉搜尋樹和二叉樹的最近公共祖先二叉樹
- 【leetcode 簡單】 第六十八題 二叉搜尋樹的最近公共祖先LeetCode
- lc235.二叉搜尋樹的最近公共祖先【①分別得到祖先序列,然後比較;②***同時查詢,找出分岔結點】
- 236、二叉樹的最近公共祖先 | 演算法(leetcode,附思維導圖 + 全部解法)300題二叉樹演算法LeetCode
- 二叉樹:距離最近的共同祖先二叉樹
- 8.3考試總結(NOIP模擬19)[最長不下降子序列·完全揹包問題·最近公共祖先]
- 程式碼隨想錄day18 || 530 二叉搜尋樹最小差,501 二叉搜尋樹眾數,236 二叉搜尋樹最近公共祖先
- LeetCode 236. 二叉樹的最近公共祖先 極限效能演算法 比LeetCode 99%還快50倍LeetCode二叉樹演算法
- Day21 | 530.二叉搜尋樹的最小絕對差、501.二叉搜尋樹中的眾數 、236. 二叉樹的最近公共祖先二叉樹
- JZ-076-樹中兩個節點的最低公共祖先
- lca
- 程式碼隨想錄演算法訓練營day18 |530.二叉搜尋樹的最小絕對差 501.二叉搜尋樹中的眾數 236. 二叉樹的最近公共祖先演算法二叉樹
- 求 LCA
- [PTA]day 5
- day5
- RMQ求lcaMQ
- 程式碼隨想錄演算法訓練營day19| 235. 二叉搜尋樹的最近公共祖先 701.二叉搜尋樹中的插入操作 450.刪除二叉搜尋樹中的節點演算法
- 程式碼隨想錄演算法訓練營day22 | leetcode 235. 二叉搜尋樹的最近公共祖先、701. 二叉搜尋樹中的插入操作、450. 刪除二叉搜尋樹中的節點演算法LeetCode
- Day5——索
- Day5SSRF
- DFN 序求 LCA