NOIP 2007 树网的核
题意
哇,太长了。
题解
显然,树的直径不唯一但一定相交并且各个·直径的中点汇聚于同一处。
进一步得到一个推论,任意一个直径上求出的偏心距都相等。
原题中(n<=100)我们发现n的范围有点小。直接上暴力。
floyed预处理一下。找到树的直径。
暴力枚举树网的核再暴力枚举偏心距(至于具体怎么枚举看代码)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 int n,s,f[305][305],ans=999999999; 8 int main(){ 9 scanf("%d%d",&n,&s); 10 memset(f,0x3f,sizeof(f)); 11 for(int i=1;i<=n;i++){ 12 f[i][i]=0; 13 } 14 for(int i=1,u,v,w;i<=n-1;i++){ 15 scanf("%d%d%d",&u,&v,&w); 16 f[u][v]=f[v][u]=w; 17 } 18 for(int x=1;x<=n;x++) 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=n;j++){ 21 f[i][j]=min(f[i][j],f[i][x]+f[x][j]); 22 } 23 int l,r,maxx=0; 24 for(int i=1;i<=n;i++) 25 for(int j=1;j<=n;j++){ 26 if(f[i][j]>maxx){ 27 maxx=f[i][j]; 28 l=i; 29 r=j; 30 } 31 } 32 for(int i=1;i<=n;i++) 33 for(int j=1;j<=n;j++){ 34 if(f[i][j]<=s&&f[l][i]+f[i][j]+f[j][r]==f[l][r]){ 35 int tmp=0; 36 for(int x=1;x<=n;x++){ 37 tmp=max(tmp,(f[x][i]+f[x][j]-f[i][j])/2); 38 } 39 ans=min(ans,tmp); 40 } 41 } 42 printf("%d",ans); 43 return 0; 44 }
但是当(n<=300000 SDOI2011消防)时以上的做法就GG了。需要考虑更优的方法。
考虑二分。(核越靠近中点答案越大)。对于我们二分的mid我们找到距离直径的两端距离大于等于mid的两点p,q之间的路径作为偏心距,判断是否成立即可。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define N 300005 6 7 int n,s,x,y,z,Max,dMax,pt,inf,ans; 8 int tot,point[N],nxt[N*2],v[N*2],c[N*2]; 9 int h[N],father[N],edge[N],leaf[N],C[N],len[N],goal[N],dis[N]; 10 bool is[N]; 11 12 void add(int x,int y,int z) 13 { 14 ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z; 15 } 16 void dfs(int x,int fa,int dep) 17 { 18 father[x]=fa;h[x]=dep; 19 if (h[x]>Max) 20 { 21 Max=h[x],pt=x; 22 } 23 bool flag=false; 24 for (int i=point[x];i;i=nxt[i]) 25 if (v[i]!=fa) 26 { 27 dfs(v[i],x,dep+c[i]),flag=true; 28 edge[v[i]]=c[i]; 29 } 30 if (!flag) leaf[++leaf[0]]=x; 31 } 32 void chain(int x) 33 { 34 while (h[x]!=0) 35 { 36 is[x]=true; 37 C[++C[0]]=x; 38 x=father[x]; 39 } 40 C[++C[0]]=x;is[x]=true; 41 } 42 void length(int x,int fa,int dep,int st) 43 { 44 dis[st]=max(dis[st],dep); 45 for (int i=point[x];i;i=nxt[i]) 46 if (v[i]!=fa&&!is[v[i]]) 47 length(v[i],x,dep+c[i],st); 48 } 49 bool check(int mid) 50 { 51 for (int i=1;i<=C[0];++i) 52 if (dis[C[i]]<=mid) len[C[i]]=dis[C[i]]-mid; 53 else return false; 54 55 len[C[1]]=len[C[C[0]]]=mid; 56 for (int i=1;i<=C[0];++i) 57 if (len[C[i]]>=edge[C[i]]) 58 { 59 len[C[i+1]]=min(len[C[i+1]],len[C[i]]-edge[C[i]]); 60 len[C[i]]=inf; 61 } 62 else break; 63 for (int i=C[0];i>=1;--i) 64 if (len[C[i]]>=edge[C[i-1]]) 65 { 66 len[C[i-1]]=min(len[C[i-1]],len[C[i]]-edge[C[i-1]]); 67 len[C[i]]=inf; 68 } 69 else break; 70 int l=0,a=0,b=0; 71 for (int i=1;i<=C[0];++i) 72 if (len[C[i]]!=inf) {a=i;break;} 73 for (int i=C[0];i>=1;--i) 74 if (len[C[i]]!=inf) {b=i;break;} 75 for (int i=a;i<b;++i) l+=edge[C[i]]; 76 if (l<=s) return true; 77 else return false; 78 } 79 int find() 80 { 81 int l=0,r=dMax,mid,ans; 82 while (l<=r) 83 { 84 mid=(l+r)>>1; 85 if (check(mid)) ans=mid,r=mid-1; 86 else l=mid+1; 87 } 88 return ans; 89 } 90 int main() 91 { 92 scanf("%d%d",&n,&s); 93 for (int i=1;i<n;++i) 94 { 95 scanf("%d%d%d",&x,&y,&z); 96 add(x,y,z);add(y,x,z);dMax+=z; 97 } 98 dfs(1,0,0); 99 leaf[0]=1,h[pt]=0,Max=0,edge[pt]=0,dfs(pt,0,0); 100 chain(pt); 101 memset(len,127,sizeof(len));inf=len[0]; 102 for (int i=1;i<=C[0];++i) 103 length(C[i],0,0,C[i]); 104 ans=find(); 105 printf("%d\n",ans); 106 }