点分治
预备知识
树的重心:删去这个点后,森林中所有树节点的最大值最小
点分治过程
具体实现
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 10005 struct Edge{ int next,to,w; }edge[maxn*2]; int fi[maxn],se,n,k,size[maxn],root,ma[maxn],sum,ans,depth[maxn]; int b[maxn],si; bool vis[maxn]; inline void add_edge(int u,int v,int w){ edge[++se].next=fi[u],edge[se].to=v,edge[se].w=w,fi[u]=se, edge[++se].next=fi[v],edge[se].to=u,edge[se].w=w,fi[v]=se; } int cal(int x){ } void getroot(int x,int f){//找到当前树的重心 size[x]=1,ma[x]=0; for(int i=fi[x];i;i=edge[i].next){ int v=edge[i].to; if(v==f||vis[v])continue;//vis[v]=1表示不属于本棵树 getroot(v,x); size[x]+=size[v];ma[x]=max(ma[x],size[v]);//ma[x]为按x分开后最大子树的节点树 } ma[x]=max(ma[x],sum-size[x]);//sum-size[x]为父节点连接的子树的大小 if(ma[x]<ma[root])root=x;//更新重心 } void solve(int x){ ans+=cal(x);vis[x]=1;//先统计过重心的答案 for(int i=fi[x];i;i=edge[i].next){ int v=edge[i].to; if(vis[v])continue; sum=size[v];root=0; getroot(v,0);//递归找子树的重心 solve(root);//统计子树的答案 } } int main(){ sum = ma[0] = n;//初始化 root = 0; getroot(1,0);//找重心 solve(root);//点分治 return 0; }
例题
POJ 1741 同BZOJ 1468(下面为POJ1741代码)
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 10005 struct Edge{ int next,to,w; }edge[maxn*2]; int fi[maxn],se,n,k,size[maxn],root,ma[maxn],sum,ans,depth[maxn]; int b[maxn],si; bool vis[maxn]; inline void add_edge(int u,int v,int w){ edge[++se].next=fi[u],edge[se].to=v,edge[se].w=w,fi[u]=se, edge[++se].next=fi[v],edge[se].to=u,edge[se].w=w,fi[v]=se; } void getroot(int x,int f){//找到当前树的重心 size[x]=1,ma[x]=0; for(int i=fi[x];i;i=edge[i].next){ int v=edge[i].to; if(v==f||vis[v])continue;//vis[v]=1表示不属于本棵树 getroot(v,x); size[x]+=size[v];ma[x]=max(ma[x],size[v]);//ma[x]为按x分开后最大子树的节点树 } ma[x]=max(ma[x],sum-size[x]);//sum-size[x]为父节点连接的子树的大小 if(ma[x]<ma[root])root=x;//更新重心 } void getdeep(int x,int f){ b[si++]=depth[x]; for(int i=fi[x];i;i=edge[i].next){ int v=edge[i].to; if(v==f||vis[v])continue; depth[v]=edge[i].w+depth[x];getdeep(v,x); } } int cal(int x,int s){ depth[x]=s,si=0; getdeep(x,0); sort(b,b+si); int l=0,r=si-1,ans=0; while(l<r){ if(b[l]+b[r]<=k){ ans+=r-l;l++; } else r--; } return ans; } void solve(int x){ ans+=cal(x,0);vis[x]=1;//先统计过重心的答案 for(int i=fi[x];i;i=edge[i].next){ int v=edge[i].to; if(vis[v])continue; ans-=cal(v,edge[i].w);//本题在统计答案时有重复,这里去重 sum=size[v];root=0; getroot(v,0);//递归找子树的重心 solve(root);//统计子树的答案 } } void work(){ int u,v,w;memset(fi,0,sizeof(fi)),memset(vis,0,sizeof(vis)); root=ans=se=0,sum=ma[0]=n; for(int i=1;i<n;i++)scanf("%d%d%d",&u,&v,&w),add_edge(u,v,w); getroot(1,0); solve(root); printf("%d\n",ans); } int main(){ while(~scanf("%d%d",&n,&k)&&n|k)work(); return 0; }