点分治
点分治可以用来处理有关树上路径的问题
首先选取当前子树的重心作为分治点,因为重心可保证最大的子树不超过(u/2),这样每次递归的处理下去,复杂度是(nlogn)的
求重心代码:
void getroot(int u,int par){ sz[u]=1,son[u]=0; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(vis[v] || v==par) continue; getroot(v,u); sz[u]+=sz[v]; son[u]=max(son[u],sz[v]); } son[u]=max(son[u],sum-sz[u]); if(son[u]<maxson){ maxson=son[u]; rt=u; } }
题目:
IOI2011 Race
题意:给定一棵树,每条边有边权,求一条路径,权值和等于k,且边的数量最小
solution:
记录s[i],当前点到分治中心的边数,d[i]当前点到分治中心的距离,f[i]已统计完的子树中,距分治中心距离为 i 的路径中的最小边数
处理当前子树时,直接用f数组更新答案
每次统计完一棵子树的答案之后,更新f数组
这样既可以考虑到所有的路径,又避免重复统计答案
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cmath> using namespace std; typedef long long ll; const int maxn = 1000010; const int INF = 1e9+7; int n,k,ans=INF; int sum,maxs; int h[maxn],size; struct E{ int to,next,cost; }e[maxn<<1]; void add(int u,int v,int w){ e[++size].to=v; e[size].next=h[u]; e[size].cost=w; h[u]=size; } int rt; int vis[maxn],sz[maxn],son[maxn]; int d[maxn],s[maxn],f[maxn*10],c[maxn],ton[maxn*10],tot,cnt; void getroot(int u,int par){ sz[u]=1,son[u]=0; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(v==par||vis[v]) continue; getroot(v,u); sz[u]+=sz[v]; son[u]=max(son[u],sz[v]); } son[u]=max(son[u],sum-sz[u]); if(son[u]<maxs){ maxs=son[u]; rt=u; } } void dfs(int u,int par,int chang){ d[u]=d[par]+chang; if(d[u]<=k) ton[++tot]=d[u]; s[u]=s[par]+1; c[++cnt]=u; if(k>=d[u]) ans=min(ans,f[k-d[u]]+s[u]); for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(vis[v]||v==par) continue; dfs(v,u,e[i].cost); } } void solve(int u){ vis[u]=1; tot=0; f[0]=0; d[u]=0; s[u]=0; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(vis[v]) continue; cnt=0; dfs(v,u,e[i].cost); for(int j=1;j<=cnt;++j){ if(d[c[j]]>k) continue; f[d[c[j]]]=min(f[d[c[j]]],s[c[j]]); } } for(int i=1;i<=tot;++i) f[ton[i]]=INF; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(vis[v]) continue; sum=sz[v],maxs=INF,rt=0; getroot(v,0); solve(rt); } } ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;} int main(){ memset(h,-1,sizeof(h)); n=read(),k=read(); int u,v; ll w; for(int i=1;i<=1000000;++i) f[i]=INF; for(int i=1;i<n;++i){ u=read(),v=read(),w=read(); u++,v++; add(u,v,w),add(v,u,w); } sum=n; maxs=INF; getroot(1,0); solve(rt); printf("%d\n",ans==INF?-1:ans); return 0; }
Spoj 1825 Free tour II