POJ 1741 树上 点的 分治
题意就是求树上距离小于等于K的点对有多少个
n2的算法肯定不行,因为1W个点
这就需要分治。可以看09年漆子超的论文
本题用到的是关于点的分治。
一个重要的问题是,为了防止退化,所以每次都要找到树的重心然后分治下去,所谓重心,就是删掉此结点后,剩下的结点最多的树结点个数最小。
每次分治,我们首先算出重心,为了计算重心,需要进行两次dfs,第一次把以每个结点为根的子树大小求出来,第二次是从这些结点中找重心
找到重心后,需要统计所有结点到重心的距离,看其中有多少对小于等于K,这里采用的方法就是把所有的距离存在一个数组里,进行快速排序,这是nlogn的,然后用一个经典的相向搜索O(n)时间内解决。但是这些求出来满足小于等于K的里面只有那些路径经过重心的点对才是有效的,也就是说在同一颗子树上的肯定不算数的,所以对每颗子树,把子树内部的满足条件的点对减去。
最后的复杂度是n logn logn 其中每次快排是nlogn 而递归的深度是logn
友情链接:~~http://blog.csdn.net/sdj222555/article/details/7893862
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 11111
#define MAXM 55555
#define INF 1000000000
using namespace std;
struct EDGE
{
int v, next, w;
}edge[MAXM];
int head[MAXN], e;
int n, k, vis[MAXN], ans, root, num;
void init() // 清空初始值
{
memset(vis,,sizeof(vis));
memset(head,-,sizeof(head));
e=ans=;
}
void add(int u,int v,int w) // 边表 加边
{
edge[e].v=v;
edge[e].w=w;
edge[e].next=head[u];
head[u]=e++;
}
int mx[MAXN], size[MAXN], mi, dis[MAXN];
void dfssize(int u, int fa) //处理以u为顶的子树的大小 fa是其父节点
{
size[u] = ;
mx[u] = ;
for(int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].v;
if(v != fa && !vis[v])
{
dfssize(v, u);
size[u] += size[v];
if(size[v] > mx[u]) mx[u] = size[v];
}
}
}
void dfsroot(int r,int u,int fa)//求重心所谓重心是指删去该点后
{ //所形成的子树的节点数最大的最小
if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
if(mx[u]<mi) mi=mx[u],root=u;
for(int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].v;
if(v!=fa&&!vis[v]) dfsroot(r,v,u);
}
}
void dfsdis(int u, int d, int fa) //求所有点到达重心的距离 即dis
{
dis[num++] = d;
for(int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].v;
if(v != fa && !vis[v]) dfsdis(v, d + edge[i].w, u);
}
}
int calc(int u,int d)
{
int ret=;
num=;
dfsdis(u,d,);
sort(dis,dis+num);
int i=,j=num-;
while(i<j) //经典
{
while(dis[i]+dis[j] > k && i < j) j--;
ret+=j-i;
i++;
}
return ret;
}
void dfs(int u)
{
mi = n;
dfssize(u, ); // 子树大小
dfsroot(u, u, ); // 重心 求完之后 root 即为重心
ans+=calc(root,);//经过root的并且满足要求的点对数(这时候会出现重边)
vis[root]=;
for(int i = head[root]; i != -; i = edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
ans-=calc(v,edge[i].w);//v是root的son 以v,edge[i].w继续向下深搜
//若这样还是满足要求(经过son并且满足要求的点对数,
//这就是重边的情况,这时将它减掉)
dfs(v);//继续处理root的son,情况同上
}
}
}
int main()
{
while(scanf("%d%d", &n, &k) != EOF)
{
if(!n && !k) break; // 不到终止条件
init();
int u, v, w;
for(int i = ; i < n - ; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dfs();
printf("%d\n", ans);
}
return ;
}
附上图解:(图是手绘的,见谅)
当一遍处理的时候 ,root等于1,这时候ans会把5-1-2这样的点对加进去,所以我们以2,dis[2](dis[2]=1)再深搜,若是再这样的条件下还满足条件(过2节点,【此时dis值没变】,还满足小于等于7,说明他就是重边,应该减掉,而对于2-5这样合法的会在dfs[2]的时候,处理好)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 11111
#define MAXM 55555
struct EDGE{
int v,w,next;
}edge[MAXM];
int n,k,vis[MAXN],mx[MAXN],e,ans;
int root,num,dis[MAXN],head[MAXN],size[MAXN],mi;
void init(){
memset(vis,,sizeof(vis));
memset(head,-,sizeof(head));
ans=e=;
}
void add(int u,int v,int w){
edge[e].v=v;edge[e].w=w;
edge[e].next=head[u];head[u]=e++;
}
void dfssize(int u,int fa){
size[u]=;mx[u]=;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfssize(v,u);
size[u]+=size[v];
if(size[v]>mx[u]) mx[u]=size[v];
}
}
}
void dfsroot(int r,int u,int fa){
if(size[r]-size[u]>mx[u]) mx[u]=size[r]-size[u];
if(mx[u]<mi){ mi=mx[u];root=u; }
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfsroot(r,v,u);
}
}
}
void dfsdis(int u,int d,int fa){
dis[num++]=d;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].v;
if(v!=fa&&!vis[v]){
dfsdis(v,d+edge[i].w,u);
}
}
}
int calc(int u,int d){
int ret=;
num=;
dfsdis(u,d,);sort(dis,dis+num);
int i=,j=num-;
while(i<j){
while(dis[i]+dis[j]>k&&i<j) j--;
ret+=j-i;
i++;
}
return ret;
}
void dfs(int u)
{
mi=n;
dfssize(u,);dfsroot(u,u,);
ans+=calc(root,);
vis[root]=;
for(int i=head[root];i!=-;i=edge[i].next){
int v=edge[i].v;
if(!vis[v]){
ans-=calc(v,edge[i].w);
dfs(v);
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(!n&&!k) break;
init();int u,v,w;
for(int i=;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs();
printf("%d\n",ans);
}
return ;
}
另附网址:09年漆子超论文 提取密码:95tu
POJ 1741 树上 点的 分治的更多相关文章
- POJ 1741 树上的点分治
题目大意: 找到树上点对间距离不大于K的点对数 这是一道简单的练习点分治的题,注意的是为了防止点分治时出现最后分治出来一颗子树为一条直线,所以用递归的方法求出最合适的root点 #include &l ...
- poj 1741 树的点分治(入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 18205 Accepted: 5951 Description ...
- POJ 1741 树的点分治
题目大意: 树上找到有多少条路径的边权值和>=k 这里在树上进行点分治,需要找到重心保证自己的不会出现过于长的链来降低复杂度 #include <cstdio> #include & ...
- POJ 1741 Tree 树的分治
原题链接:http://poj.org/problem?id=1741 题意: 给你棵树,询问有多少点对,使得这条路径上的权值和小于K 题解: 就..大约就是树的分治 代码: #include< ...
- POJ 1741 Tree【树分治】
第一次接触树分治,看了论文又照挑战上抄的代码,也就理解到这个层次了.. 以后做题中再慢慢体会学习. 题目链接: http://poj.org/problem?id=1741 题意: 给定树和树边的权重 ...
- poj 1741 Tree(点分治)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 15548 Accepted: 5054 Description ...
- POJ 1741 Tree (树分治入门)
Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8554 Accepted: 2545 Description ...
- POJ 1741 Tree (点分治)
Tree Time Limit: 1000MS Memory ...
- POJ 1741 Tree 树的分治(点分治)
题目大意:给出一颗无根树和每条边的权值,求出树上两个点之间距离<=k的点的对数. 思路:树的点分治.利用递归和求树的重心来解决这类问题.由于满足题意的点对一共仅仅有两种: 1.在以该节点的子树中 ...
随机推荐
- PE刷题记
PE 中文翻译 最喜欢做这种很有意思的数学题了虽然数学很垃圾 但是这个网站的提交方式好鬼畜啊qwq 1.Multiples of 3 and 5 直接枚举 2.Even Fibonacci numbe ...
- 架包Error inflating class错误
当引用架包后,出现Error inflating class错误时通常要检测架包是否正确引用: 1.首先将你所需要的架包拷贝到工程目录下: 2.右击工程,选择Build Path-->confi ...
- Mac上面不能安装Homebrew
这个stackoverflow的答案解决了我的问题: http://stackoverflow.com/questions/18039029/mac-can-t-install-homebrew 问题 ...
- Android安卓下拉阻尼效果实现原理及简单实例
原理 这种效果是通过自定义控件的方式来实现的,我自定义了一个控件类型,这个自定义控件(PullDownDumperLayout)继承自线性布局(LinearLayout). 用户可以下拉弹出的那个 ...
- centos 更换yum源 (解决下载慢的问题)
先看有没有安装wget wget -V 如果没有执行 yum -y install wget 进行安装 然后进行配置的备份 mv /etc/yum.repos.d/CentO ...
- Linux一些常用小命令
使用xshell连接虚拟机 rz 上传的linux服务器 sz 从服务器上下载 df 查看磁盘大小 -h du 查看所有磁盘(硬盘)大小(-h 可读 -s统计当前目录的大小)du -sh free ...
- Tomcat启动报错 ERROR org.apache.struts2.dispatcher.Dispatcher - Dispatcher initialization failed
背景: 在进行Spring Struts2 Hibernate 即SSH整合的过程中遇到了这个错误! 原因分析: Bean已经被加载了,不能重复加载 原来是Jar包重复了! 情形一: Tomcat ...
- xshell通过xftp传输Windows文件到Linux:在输入put后,再摁 TAB 键,可显示当前文件夹的文件
在输入put后,再摁 TAB 键,可显示当前文件夹的文件 sftp:/home/yan> put $Recycle.Bin\ BluestacksCN\ ...
- MFC隐藏在黑暗之中的大坑
大坑一:CDC会随着窗口状态的改变而改变 void K5::OnPaint() { CDC *pDC=this->GetDC(); //CDC最好设为局部变量 ... this->Rele ...
- openjudge-1664 放苹果
总时间限制: 1000ms 内存限制: 65536kB 描述 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. 输 ...