洛谷 P1099 树网的核(树的直径,树上尺取)
传送门
传送门(加强版)
解题思路
这道题是我在学校里讲课的题,放上课件下载地址:https://files.cnblogs.com/files/yinyuqin/树网的核.ppt。
感觉课件里讲的还是比较详细的。
先说一下n^3做法:Floyd预处理出树中任意两点之间的距离,然后在直径上暴力枚举核的左右端点,再枚举每个点,则这个点到核的距离为(dis[d[i]][k]+dis[d[j]][k]-dis[d[i]][d[j]])/2。画图即可推出来。
o(n^3)做法原题稳过,但是我们追求更高效的做法。
o(n^2)需要用到尺取思想,在核的左端点确定时,核长度越大结果越优,这样就可以o(n)枚举树网的核,然后枚举树网的和上的每个节点,对于核上的每个节点所能到达的最远的点和核两端到直径两端的距离去max,即为这个树网的核的ECC。我们需要提前预处理出树直径上每个点不经过直径所能到达的最远点的距离。
对于o(n)做法,我们从o(n^2)做法改进,很显然枚举树网的核没法优化,但我们可以用单调队列o(1)处理出树网的核这段区间内的最远距离。
另外,单调队列也可以优化掉。
我们发现这样一个性质,影响ECC的答案的点只有这几个情况:
- 核两端点到直径两端点的距离
- 直径外的点到直径的最远距离
对于第一个情况,我们枚举树网的核时可以直接求出来;
对于第二种情况,很显然除了离直径最远的点,其他的点对最终答案不会产生影响,还是可以画图理解。所以只需在最后取一个max即可。
AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 const int maxn=305; 8 int n,s,dis[maxn][maxn],m[maxn][maxn],d1,d2,d[maxn],num,ans=0x3f3f3f3f; 9 bool ok(int u,int fa,int d2){ 10 for(int i=1;i<=n;i++){ 11 if(i==d2&&m[u][i]){ 12 d[++num]=d2; 13 d[++num]=u; 14 return 1; 15 } 16 if(i!=fa&&m[u][i]){ 17 if(ok(i,u,d2)){ 18 d[++num]=u; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 void findd(int d1,int fa,int d2){ 26 for(int i=1;i<=n;i++){ 27 if(i==d2&&m[d1][i]){ 28 d[++num]=d2; 29 d[++num]=d1; 30 return; 31 } 32 if(i!=fa&&m[d1][i]){ 33 if(ok(i,d1,d2)){ 34 d[++num]=d1; 35 return; 36 } 37 } 38 } 39 } 40 int main() 41 { 42 memset(dis,0x3f,sizeof(dis)); 43 cin>>n>>s; 44 for(int i=1;i<n;i++){ 45 int u,v,value; 46 cin>>u>>v>>value; 47 m[u][v]=m[v][u]=dis[u][v]=dis[v][u]=value; 48 } 49 for(int i=0;i<=n;i++) dis[i][i]=0; 50 for(int k=1;k<=n;k++){ 51 for(int i=1;i<=n;i++){ 52 for(int j=1;j<=n;j++){ 53 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); 54 } 55 } 56 } 57 for(int i=1;i<=n;i++){ 58 for(int j=1;j<=n;j++){ 59 if(dis[i][j]>dis[d1][d2]) d1=i,d2=j; 60 } 61 } 62 findd(d1,-1,d2); 63 for(int i=1;i<=num;i++) 64 for(int j=i;j<=num;j++){ 65 int ecc=ans; 66 if(dis[d[i]][d[j]]<=s){ 67 ecc=0; 68 for(int k=1;k<=n;k++){ 69 ecc=max(ecc,(dis[d[i]][k]+dis[d[j]][k]-dis[d[i]][d[j]])/2); 70 } 71 } 72 ans=min(ans,ecc); 73 } 74 cout<<ans; 75 return 0; 76 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 const int maxn=500005; 7 using namespace std; 8 int n,s,p[maxn],d[maxn],isd[maxn],d1,d2,num,cnt,fa[maxn],maxd; 9 long long ans,anss=(long long)10*0x3f3f3f3f,dis[maxn],lend,disfa[maxn]; 10 struct node{ 11 int v,next,value; 12 }e[maxn*2]; 13 void insert(int u,int v,int value){ 14 cnt++; 15 e[cnt].v=v; 16 e[cnt].next=p[u]; 17 e[cnt].value=value; 18 p[u]=cnt; 19 } 20 void dfs(int u,int faa){ 21 for(int i=p[u];i!=-1;i=e[i].next){ 22 int v=e[i].v; 23 if(v==faa) continue; 24 fa[v]=u; 25 disfa[v]=e[i].value; 26 dis[v]=dis[u]+e[i].value; 27 if(dis[v]>dis[maxd]) maxd=v; 28 dfs(v,u); 29 } 30 } 31 long long dfsdis(int u,int fa,long long diss){ 32 long long ans=0; 33 for(int i=p[u];i!=-1;i=e[i].next){ 34 if(e[i].v!=fa){ 35 ans=max(ans,dfsdis(e[i].v,u,e[i].value)); 36 } 37 } 38 return ans+diss; 39 } 40 int main() 41 { 42 memset(p,-1,sizeof(p)); 43 cin>>n>>s; 44 for(int i=1;i<n;i++){ 45 int u,v,value; 46 scanf("%d%d%d",&u,&v,&value); 47 insert(u,v,value); 48 insert(v,u,value); 49 } 50 dfs(1,-1); 51 d1=maxd; 52 memset(dis,0,sizeof(dis)); 53 memset(disfa,0,sizeof(disfa)); 54 memset(fa,-1,sizeof(fa)); 55 dfs(d1,-1); 56 d2=maxd; 57 for(int i=d2;i!=-1;i=fa[i]){ 58 d[++num]=i; 59 isd[i]=1; 60 } 61 for(int i=1;i<=num;i++){ 62 for(int u=p[d[i]];u!=-1;u=e[u].next){ 63 if(isd[e[u].v]) continue; 64 ans=max(ans,dfsdis(e[u].v,d[i],e[u].value)); 65 } 66 } 67 int head=1,tail=1; 68 long long len=0,lend1=dis[d2],lend2=0; 69 while(head<=num&&tail<=num){ 70 while(tail<=num&&len+disfa[d[tail]]<=s){ 71 len+=disfa[d[tail]]; 72 lend1-=disfa[d[tail]]; 73 tail++; 74 } 75 anss=min(anss,max(lend1,lend2)); 76 len-=disfa[d[head]]; 77 lend2+=disfa[d[head]]; 78 head++; 79 } 80 cout<<max(ans,anss); 81 return 0; 82 }