【传送门:BZOJ2599】
简要题意:
给出一棵n个点的树,和每条边的边权
求出所有距离为k的点对中,两个点之间的最少边数
题解:
点分治好题
设c[i]为当前点分治的时候与根的距离为i的最小边数
因为我们不能使得求出来的点对都在一棵子树里面,所以我们枚举每一棵与根相连的子树,每次求出对于这棵子树的c数组
然后再DFS一遍,更新答案
听说不能用memset来初始化c,那么就DFS将用过的位置初始化
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define INF 1<<31-1 using namespace std; struct node { int x,y,d,next; }a[410000];int len,last[210000]; void ins(int x,int y,int d) { len++; a[len].x=x;a[len].y=y;a[len].d=d; a[len].next=last[x];last[x]=len; } int tot[210000],root,sum,ms[210000]; bool v[210000]; void getroot(int x,int fa) { tot[x]=1;ms[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa&&v[y]==false) { getroot(y,x); tot[x]+=tot[y]; ms[x]=max(ms[x],tot[y]); } } ms[x]=max(ms[x],sum-tot[x]); if(ms[root]>ms[x]) root=x; } int ans,k; int c[1100000]; int dep[210000],d[210000]; void cal(int x,int fa) { if(d[x]<=k) ans=min(ans,c[k-d[x]]+dep[x]); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa&&v[y]==false) { dep[y]=dep[x]+1; d[y]=d[x]+a[k].d; cal(y,x); } } } void change(int x,int fa,int t) { if(d[x]<=k) { if(t==0) c[d[x]]=min(c[d[x]],dep[x]); else c[d[x]]=INF; } for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(y!=fa&&v[y]==false) { change(y,x,t); } } } void solve(int x) { v[x]=true;c[0]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { dep[y]=1;d[y]=a[k].d; cal(y,0); change(y,0,0); } } for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { change(y,0,1); } } for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { sum=tot[y]; root=0;getroot(y,x); solve(root); } } } int main() { int n; scanf("%d%d",&n,&k); len=0;memset(last,0,sizeof(last)); for(int i=1;i<n;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); x++;y++; ins(x,y,d);ins(y,x,d); } memset(v,false,sizeof(v)); memset(c,63,sizeof(c));c[0]=0; ans=sum=ms[0]=n; root=0;getroot(1,0); solve(root); if(ans!=n) printf("%d ",ans); else printf("-1 "); return 0; }