【点分治】【POJ 1741】【cogs 1714】树上的点对
2015-06-10 18:05
375 查看
1714. [POJ1741][男人八题]树上的点对
[code]★★★ 输入文件:poj1741_tree.in 输出文件:poj1741_tree.out 简单对比 时间限制:1 s 内存限制:256 MB
【题目描述】
给一棵有n个节点的树,每条边都有一个长度(小于1001的正整数)。
定义dist(u,v)=节点u到节点v的最短路距离。
给出一个整数k,我们称顶点对(u,v)是合法的当且仅当dist(u,v)不大于k。
写一个程序,对于给定的树,计算有多少对顶点对是合法的。
【输入格式】
输入包含多组数据。
每组数据的第一行有两个整数N,K(N<=10000)。接下来N-1行每行有三个整数u,v,l,代表节点u和v之间有一条长度l的无向边。
输入结束标志为N=K=0.
【输出格式】
对每组数据输出一行一个正整数,即合法顶点对的数量。
【样例输入】
[code]5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0
【样例输出】
[code]8
【来源】
[code]POJ 1741 Tree 男人八题 Problem E
题解:
[code]题目背景: 这是楼教主的“男人八题”的E题,当时还并没有点分这种算法,于是被楼教主开发了出来。。
这是点分治裸题。。漆子超论文上有讲过。
我们其实就是把整棵树的问题转化为在各个子树和子树之间的问题,于是我们就可以分治。
具体做法是先找重心,然后计算子树中各个点到当前子树的根的距离,然后考虑跨越子树的情况,最后加到一起就好了。
Code:
[code]#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define N 10010 using namespace std; struct Edge{ int v,next,l; }edge[2*N]; int n,k,root,size,num,ans; int head ,son ,h ,dis ,d ; bool vis ={0}; int in(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } void add(int u,int v,int l){ edge[++num].v=v; edge[num].l=l; edge[num].next=head[u]; head[u]=num; } void Focus(int x,int last){ son[x]=1; h[x]=0; for (int i=head[x]; i; i=edge[i].next){ int v=edge[i].v; if (vis[v] || v==last) continue; Focus(v,x); son[x]+=son[v]; h[x]=max(h[x],son[v]); } h[x]=max(h[x],size-son[x]); if (h[x]<h[root]) root=x; } void Dis(int x,int last){ d[++d[0]]=dis[x]; for (int i=head[x]; i; i=edge[i].next){ int v=edge[i].v; if (vis[v] || v==last) continue; dis[v]=dis[x]+edge[i].l; Dis(v,x); } } int Calc(int x,int p){ dis[x]=p,d[0]=0; Dis(x,0); sort(d+1,d+d[0]+1); int l=1,r=d[0],s=0; // cout<<"now: "<<x<<endl; while (l<r){ if (d[l]+d[r]<=k) s+=(r-l),l++; else r--; // cout<<"L: "<<l<<" "<<"R: "<<r<<" "<<"sum: "<<s<<endl; } // cout<<"now: "<<x<<" "<<"sum: "<<s<<endl; return s; } void Point(int x){ vis[x]=1; ans+=Calc(x,0); for (int i=head[x]; i; i=edge[i].next){ int v=edge[i].v; if (vis[v]) continue; ans-=Calc(v,edge[i].l); root=0; size=h[root]=son[v]; Focus(v,0); Point(root); } // cout<<"ans--->"<<ans<<endl; } void Work(){ root=0; size=h[root]=n; memset(vis,0,sizeof(vis)); Focus(1,0); Point(root); } int main(){ while (n=in(),k=in()){ if (!n && !k) break; num=ans=0; memset(head,0,sizeof(head)); for (int i=1; i<n; i++){ int u=in(),v=in(),l=in(); add(u,v,l),add(v,u,l); } Work(); printf("%d\n",ans); } return 0; }
相关文章推荐
- Java基础加强总结(二)——泛型
- 网狐服务端网站常见部署问题
- 【总结】Xcode编译问题整理
- IOS 宏定义
- php正则表达式—实战
- ios-通知:闹钟之关于设置闹钟后,点击app图标进入程序不播放音乐的问题(已解决)
- Jackson学习笔记(三)
- 初学Hibernate心得体会
- socket编程之bind函数可能遇到的错误及解决办法
- StringTokenizer使用
- MySQL Study之--Mysql启动失败“mysql.host”
- leetcode--Restore IP Addresses
- c# singleton
- WinSock学习笔记
- 将UIImage转化成NSData在转化成NSString(将UIImage转化成Base64的字节流)
- echarts.js使用方法
- 自己犯的一些低级错误整理
- Maven构建过程中报“非法字符: \65279 ”错误的解决方法
- Ubuntu 14.04 Linux内核版本3.16.0内核模块开发Makefile文件样板
- Python基础:24with语句