【点分治】【路径小于等于k的条数】【路径恰好等于k是否存在】
|
POJ1741:Tree
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. 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. Output For each test case output the answer on a single line.
Sample Input 5 4 Sample Output 8 Source |
求树上路径距离小于等于k的条数。
点分治即是将树拆开,dfs处理每棵子树的过程。每次找到当前子树的重心,从重心开始分治(即是放弃父亲,不再管之前的祖先。
这道题可以用容斥计算贡献。先统计出当前整棵树的答案,减去每棵子树重复计算的不成立的答案。【注意】这里的答案都是指经过当前树的根节点的路径。

如图,如果k是4,,直接统计子树1的答案,会把1到3和1到4这条路径统计进去,而这是不成立的,所以在统计子树2多余答案时,先把1到2这条边权加进2的dis,再统计子树2中满足条件的点对,减去即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std; int bal, asize, sum, n, k, ans; int tov[], nex[], h[], stot, w[]; void add ( int u, int v, int s ) {
tov[++stot] = v;
w[stot] = s;
nex[stot] = h[u];
h[u] = stot;
} int siz[], vis[]; void find_root ( int u, int f ) {
siz[u] = ;
int res = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f || vis[v] ) continue;
find_root ( v, u );
siz[u] += siz[v];
res = max ( res, siz[v] );
}
res = max ( res, sum - siz[u] );
if ( res < asize ) {
asize = res, bal = u;
}
} int dep[], dis[]; void get_dep ( int u, int f ) {
dep[++dep[]] = dis[u];
siz[u] = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f || vis[v] ) continue;
dis[v] = dis[u] + w[i];
get_dep ( v, u );
siz[u] += siz[v];
}
} int cal ( int u, int now ) {
dis[u] = now; dep[] = ;
get_dep ( u, );
sort ( dep + , dep + dep[] + );
int tmp = , l = , r = dep[];
while ( l < r ) {
if ( dep[l] + dep[r] <= k ) {
tmp += r - l; l ++;
} else r --;
}
return tmp;
} void work ( int u ) {
ans += cal ( u, );
vis[u] = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] ) continue;
ans -= cal ( v, w[i] );
sum = siz[v];
asize = 0x3f3f3f3f;
find_root ( v, u );
work ( bal );
}
} int main ( ) {
while ( scanf ( "%d%d", &n, &k ) == ) {
if ( n == && k == ) break;
asize = 0x3f3f3f3f;
stot = ; ans = ;
memset ( h, , sizeof ( h ) );
memset ( dis, , sizeof ( dis ) );
memset ( vis, , sizeof ( vis ) );
for ( int i = ; i < n; i ++ ) {
int a, b, c;
scanf ( "%d%d%d", &a, &b, &c );
add ( a, b, c );
add ( b, a, c );
}
sum = n;
find_root ( , );
work ( bal );
printf ( "%d\n", ans );
}
return ;
}
洛谷P3806: 【模板】点分治1
题目背景
感谢hzwer的点分治互测。
题目描述
给定一棵有n个点的树
询问树上距离为k的点对是否存在。
输入输出格式
输入格式:
n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径
接下来m行每行询问一个K
输出格式:
对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)
输入输出样例
说明
对于30%的数据n<=100
对于60%的数据n<=1000,m<=50
对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000
这道题和上一道实质一样,不过我换了种写法。在统计当前树答案时,进入每棵子树,先把这棵子树的答案与之前计算过的子树答案(exist)进行比对,如果可以就更新答案,再更新exist数组,这样可以保证不会出现上面图示情况,因为计算当前子树时,不会出现子树内部互相更新的情况。
#include<iostream>
#include<cstdio>
using namespace std; int n, m, qus[]; int stot, tov[], nex[], h[], w[];
void add ( int u, int v, int s ) {
tov[++stot] = v;
w[stot] = s;
nex[stot] = h[u];
h[u] = stot;
} int siz[], asize, size, root, vis[], maxp[];
void findroot ( int u, int f ) {
siz[u] = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f || vis[v] ) continue;
findroot ( v, u );
siz[u] += siz[v];
maxp[u] = max ( maxp[u], siz[v] );
}
maxp[u] = max ( maxp[u], size - siz[u] );
if ( maxp[u] < maxp[root] ) root = u;
} int dep[], dis[];
void getdis ( int u, int f ) {
dep[++dep[]] = dis[u];
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f || vis[v] ) continue;
dis[v] = dis[u] + w[i];
getdis ( v, u );
}
} bool judge[], exist[];
int q[], p;
void count ( int u ) {
int p = ;
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] ) continue; dep[] = ; dis[v] = w[i];
getdis ( v, u ); for ( int j = dep[]; j; j -- )
for ( int k = ; k <= m; k ++ )
if ( qus[k] >= dep[j] )
judge[k] |= exist[qus[k]-dep[j]]; for ( int j = dep[]; j; j -- )
q[++p] = dep[j], exist[dep[j]] = ;
}
for ( int i = ; i <= p; i ++ )
exist[q[i]] = ;
} void work ( int u ) {
vis[u] = ; exist[] = ;
count ( u );
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( vis[v] ) continue;
size = siz[v]; root = ;
findroot ( v, );
work ( root );
}
} int main ( ) {
freopen ( "a.in", "r", stdin );
freopen ( "a.out", "w", stdout );
scanf ( "%d%d", &n, &m );
for ( int i = ; i < n; i ++ ) {
int a, b, c;
scanf ( "%d%d%d", &a, &b, &c );
add ( a, b, c );
add ( b, a, c );
} for ( int i = ; i <= m; i ++ )
scanf ( "%d", &qus[i] ); size = n; maxp[root] = n;
findroot ( , );
work ( root ); for ( int i = ; i <= m; i ++ )
if ( judge[i] )
printf ( "AYE\n" );
else printf ( "NAY\n" );
return ;
}
【点分治】【路径小于等于k的条数】【路径恰好等于k是否存在】的更多相关文章
- 【分治】输出前k大的数
描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入第一行包含一个整数n,表示数组的大小.n < 100000.第二行包含n个整数,表示数组的元素,整数之间以一个空格分开.每个整 ...
- OpenJ_Bailian 7617 输出前k大的数
题目传送门 OpenJ_Bailian 7617 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大小.n < 100000.第二行包含n个 ...
- 基于快速排序思想partition查找第K大的数或者第K小的数。
快速排序 下面是之前实现过的快速排序的代码. function quickSort(a,left,right){ if(left==right)return; let key=partition(a, ...
- 7617:输出前k大的数
7617:输出前k大的数 查看 提交 统计 提问 总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 给定一个数组,统计前k大的数并且把这k个数从大到小 ...
- 输出前 k 大的数
总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大小 ...
- 求数列中第K大的数
原创 利用到快速排序的思想,快速排序思想:https://www.cnblogs.com/chiweiming/p/9188984.html array代表存放数列的数组,K代表第K大的数,mid代表 ...
- 每天一道算法题(32)——输出数组中第k小的数
1.题目 快速输出第K小的数 2.思路 使用快速排序的思想,递归求解.若键值位置i与k相等,返回.若大于k,则在[start,i-1]中寻找第k大的数.若小于k.则在[i+1,end]中寻找第k+st ...
- 输出前k大的数
总时间限制: 10000ms单个测试点时间限制:1000ms内存限制:65536kB(noi) 描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大 ...
- noi 统计前k大的数
描述 给定一个数组,统计前k大的数并且把这k个数从大到小输出. 输入 第一行包含一个整数n,表示数组的大小.n < 100000. 第二行包含n个整数,表示数组的元素,整数之间以一个空格分开.每 ...
随机推荐
- npm install ERR! code E400/E404
在安装webpack的过程中,出现了一个报错npm install ERR! code E400/E404 解决方法: 1.查看npm配置文件 是否有错误: 执行 npm config edit 查看 ...
- js判断手机端(Android手机还是iPhone手机)
/** * [isMobile 判断平台] * @param test: 0:iPhone 1:Android */ function ismobile(test){ var u = navigato ...
- 蓝色的cms企业记账管理后台模板源码——后台
链接:http://pan.baidu.com/s/1bpxKGBP 密码:suda
- (1)剑指Offer之斐波那契数列问题和跳台阶问题
一 斐波那契数列 题目描述: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项. n<=39 问题分析: 可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有 ...
- sea.js中的checkbox批量操作
<table width="100%" border="0" cellspacing="0" cellpadding="0& ...
- u-boot启动第一阶段
目标板:2440开发板 u-boot启动的第一阶段是在文件start.S中完成的,这个过程对不同硬件平台的设置是不同的.下面进入start.S _start: b reset //跳转到reset / ...
- 详细介绍Linux finger命令的使用
Linux 允许多个用户使用不同的终端同时登陆,Linux finger命令为系统管理员提供知道某个时候到底有多少用户在使用这台Linux主机的方法,对于这个简单的命令我们还是先介绍一下再举例吧. L ...
- MySQL 和 Oracle 主键自增长
1.MySQL 1)建表 auto_increment:每插入一条数据,客户表(customers)的主键id就自动增1,如下所示 create table customers -- 创建客户表 ( ...
- HDU 2112 Today(Dijkstra+map)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2112 题目大意: 经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050 ...
- HDU-2222
Keywords Search Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...