POJ 1741.Tree and 洛谷 P4178 Tree-树分治(点分治,容斥版) +二分 模板题-区间点对最短距离<=K的点对数量
POJ 1741.
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 34141 | Accepted: 11420 |
Description
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
Input
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
Sample Output
8
题意就是给你一个带边权的树,求树上最短距离<=K的点对数量。树分治模板题。
代码:
1 //树分治-点分治 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 typedef long long ll; 10 const int inf=1e9+10; 11 const int maxn=1e5+10; 12 13 int head[maxn],tot; 14 int root,allnode,ans,n,k; 15 int vis[maxn],deep[maxn],dis[maxn],siz[maxn],point[maxn];//deep[0]子节点个数(路径长度),point为重心节点 16 17 struct node{ 18 int to,next,val; 19 }edge[maxn<<1]; 20 21 void add(int u,int v,int w)//前向星存图 22 { 23 edge[tot].to=v; 24 edge[tot].next=head[u]; 25 edge[tot].val=w; 26 head[u]=tot++; 27 } 28 29 void init()//初始化 30 { 31 memset(head,-1,sizeof head); 32 memset(vis,0,sizeof vis); 33 tot=0; 34 } 35 36 void get_root(int u,int father)//重心 37 { 38 siz[u]=1;point[u]=0; 39 for(int i=head[u];~i;i=edge[i].next){ 40 int v=edge[i].to; 41 if(v==father||vis[v]) continue; 42 get_root(v,u);//递归得到子树大小 43 siz[u]+=siz[v]; 44 point[u]=max(point[u],siz[v]);//更新u节点的point 45 } 46 point[u]=max(point[u],allnode-siz[u]);//保存节点size 47 if(point[u]<point[root]) root=u;//更新当前子树的重心 48 } 49 50 void get_dis(int u,int father)//获取子树所有节点与根的距离 51 { 52 deep[++deep[0]]=dis[u]; 53 for(int i=head[u];~i;i=edge[i].next){ 54 int v=edge[i].to; 55 if(v==father||vis[v]) continue; 56 int w=edge[i].val; 57 dis[v]=dis[u]+w; 58 get_dis(v,u); 59 } 60 } 61 62 int cal(int u,int now) 63 { 64 dis[u]=now;deep[0]=0; 65 get_dis(u,0); 66 sort(deep+1,deep+deep[0]+1); 67 int all=0; 68 for(int l=1,r=deep[0];l<r;){//二分 69 if(deep[l]+deep[r]<=k){ 70 all+=r-l;l++; 71 } 72 else r--; 73 } 74 return all; 75 } 76 77 void solve(int u)//以u为重心进行计算 78 { 79 ans+=cal(u,0);//以当前u为重心的贡献 80 vis[u]=1; 81 for(int i=head[u];~i;i=edge[i].next){ 82 int v=edge[i].to; 83 if(vis[v]) continue; 84 ans-=cal(v,edge[i].val);//减去子树的影响 85 allnode=siz[v]; 86 root=0; 87 get_root(v,u); 88 solve(root); 89 } 90 } 91 92 int main() 93 { 94 while(~scanf("%d%d",&n,&k)&&n&&k){ 95 init(); 96 for(int i=1;i<n;i++){ 97 int u,v,w; 98 scanf("%d%d%d",&u,&v,&w); 99 add(u,v,w); 100 add(v,u,w); 101 } 102 root=ans=0; 103 allnode=n;point[0]=inf; 104 get_root(1,0); 105 solve(root); 106 printf("%d\n",ans); 107 } 108 return 0; 109 }
一样的东西,就只是改了一下数据范围和输入格式。
代码:
1 //树分治-点分治 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 typedef long long ll; 10 const int inf=1e9+10; 11 const int maxn=4e4+10; 12 13 int head[maxn],tot; 14 int root,allnode,ans,n,k; 15 int vis[maxn],deep[maxn],dis[maxn],siz[maxn],point[maxn];//deep[0]子节点个数(路径长度),point为重心节点 16 17 struct node{ 18 int to,next,val; 19 }edge[maxn<<1]; 20 21 void add(int u,int v,int w)//前向星存图 22 { 23 edge[tot].to=v; 24 edge[tot].next=head[u]; 25 edge[tot].val=w; 26 head[u]=tot++; 27 } 28 29 void init()//初始化 30 { 31 memset(head,-1,sizeof head); 32 memset(vis,0,sizeof vis); 33 tot=0; 34 } 35 36 void get_root(int u,int father)//重心 37 { 38 siz[u]=1;point[u]=0; 39 for(int i=head[u];~i;i=edge[i].next){ 40 int v=edge[i].to; 41 if(v==father||vis[v]) continue; 42 get_root(v,u);//递归得到子树大小 43 siz[u]+=siz[v]; 44 point[u]=max(point[u],siz[v]);//更新u节点的point 45 } 46 point[u]=max(point[u],allnode-siz[u]);//保存节点size 47 if(point[u]<point[root]) root=u;//更新当前子树的重心 48 } 49 50 void get_dis(int u,int father)//获取子树所有节点与根的距离 51 { 52 deep[++deep[0]]=dis[u]; 53 for(int i=head[u];~i;i=edge[i].next){ 54 int v=edge[i].to; 55 if(v==father||vis[v]) continue; 56 int w=edge[i].val; 57 dis[v]=dis[u]+w; 58 get_dis(v,u); 59 } 60 } 61 62 int cal(int u,int now) 63 { 64 dis[u]=now;deep[0]=0; 65 get_dis(u,0); 66 sort(deep+1,deep+deep[0]+1); 67 int all=0; 68 for(int l=1,r=deep[0];l<r;){//二分 69 if(deep[l]+deep[r]<=k){ 70 all+=r-l;l++; 71 } 72 else r--; 73 } 74 return all; 75 } 76 77 void solve(int u)//以u为重心进行计算 78 { 79 ans+=cal(u,0);//以当前u为重心的贡献 80 vis[u]=1; 81 for(int i=head[u];~i;i=edge[i].next){ 82 int v=edge[i].to; 83 if(vis[v]) continue; 84 ans-=cal(v,edge[i].val);//减去子树的影响 85 allnode=siz[v]; 86 root=0; 87 get_root(v,u); 88 solve(root); 89 } 90 } 91 92 int main() 93 { 94 scanf("%d",&n); 95 init(); 96 for(int i=1;i<n;i++){ 97 int u,v,w; 98 scanf("%d%d%d",&u,&v,&w); 99 add(u,v,w); 100 add(v,u,w); 101 } 102 scanf("%d",&k); 103 root=ans=0; 104 allnode=n;point[0]=inf; 105 get_root(1,0); 106 solve(root); 107 printf("%d\n",ans); 108 return 0; 109 }