题意:求树中点对距离<=k的无序点对个数。
解题关键:树上点分治,这个分治并没有传统分治的合并过程,只是分成各个小问题,并将各个小问题的答案相加即可,也就是每层的复杂度并不在合并的过程,是在每层的处理过程。
此题维护的是树上路径,考虑点分治。
点分治的模板题,首先设点x到当前子树跟root的距离为,则满足${d_x} + {d_y} \le k$可以加进答案,但是注意如果x,y在同一棵子树中,就要删去对答案的贡献,因为x,y会在其所在的子树中在计算一次。同一棵子树中不必考虑是否在其同一棵子树中的问题,因为无论是否在他的同一棵子树,都会对他的父节点产生影响。而这些影响都是无意义的。
注意无根树转有根树的过程,需要选取树的重心防止复杂度从$O(n{\log ^2}n)$退化为$O({n^2})$
复杂度:$O(n{\log ^2}n)$
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 #define inf 0x3f3f3f3f 8 #define maxn 10004 9 using namespace std; 10 typedef long long ll; 11 int head[maxn],cnt,n,k,ans,size,s[maxn],f[maxn],root,depth[maxn],num;//vis代表整体的访问情况,每个dfs不应该只用vis来存储 12 bool vis[maxn]; 13 struct edge{ 14 int to,w,nxt; 15 }e[maxn<<1]; 16 void add_edge(int u,int v,int w){ 17 e[cnt].to=v; 18 e[cnt].w=w; 19 e[cnt].nxt=head[u]; 20 head[u]=cnt++; 21 } 22 23 inline int read(){ 24 char k=0;char ls;ls=getchar();for(;ls<‘0‘||ls>‘9‘;k=ls,ls=getchar()); 25 int x=0;for(;ls>=‘0‘&&ls<=‘9‘;ls=getchar())x=(x<<3)+(x<<1)+ls-‘0‘; 26 if(k==‘-‘)x=0-x;return x; 27 } 28 29 void get_root(int u,int fa){//get_root会用到size 30 s[u]=1;f[u]=0;//f是dp数组 31 for(int i=head[u];i!=-1;i=e[i].nxt){ 32 int v=e[i].to; 33 if(v==fa||vis[v]) continue; 34 get_root(v,u); 35 s[u]+=s[v]; 36 f[u]=max(f[u],s[v]); 37 } 38 f[u]=max(f[u],size-f[u]); 39 root=f[root]>f[u]?u:root; 40 } 41 42 void get_depth_size(int u,int fa,int dis){//同时获取size和depth 43 depth[num++]=dis; 44 s[u]=1; 45 for(int i=head[u];i!=-1;i=e[i].nxt){ 46 int v=e[i].to; 47 if(v==fa||vis[v]) continue; 48 get_depth_size(v,u,dis+e[i].w); 49 s[u]+=s[v]; 50 } 51 } 52 53 int calc(int u,int fa,int w){ 54 num=0; 55 get_depth_size(u,fa,w); 56 sort(depth,depth+num); 57 int ret=0; 58 for(int l=0,r=num-1;l<r;){ 59 if(depth[l]+depth[r]<=k) ret+=r-l++; 60 else r--; 61 } 62 return ret; 63 } 64 65 void work(int u){ 66 vis[u]=true; 67 ans+=calc(u,-1,0); 68 for(int i=head[u];i!=-1;i=e[i].nxt){ 69 int v=e[i].to; 70 if(vis[v]) continue; 71 ans-=calc(v,u,e[i].w); 72 size=s[v],root=0; 73 get_root(v,u); 74 work(root); 75 } 76 } 77 78 void init(){ 79 memset(vis,false, sizeof vis); 80 memset(head,-1,sizeof head); 81 ans=cnt=0; 82 } 83 84 85 int main(){ 86 int a,b,c; 87 f[0]=inf; 88 while(scanf("%d%d",&n,&k)&&(n||k)){ 89 init(); 90 for(int i=0;i<n-1;i++){ 91 a=read(),b=read(),c=read(); 92 add_edge(a,b,c); 93 add_edge(b,a,c); 94 } 95 size=n,root=0; 96 get_root(1,-1); 97 work(root); 98 printf("%d\n",ans); 99 } 100 return 0; 101 102 }
时间: 2024-11-06 23:32:32