【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治
题目描述
.jpg)
输入
输出
|
样例输入T1 7 6 样例输出T1 13 |
样例输入T2 7 6 样例输出T2 52 |
样例输入T3 7 6 样例输出T3 13 |
样例输入T4 7 6 样例输出T4 5 |
题解
各种树上问题处理方法
题目中描述“每对农场间有且仅有一条路径”,所以这个图其实就是一棵树,也就是说m是没用的。
另外路径方向除了T1都没用。。。
T1:将询问离线,按时间排序,用带权并查集维护每个点的祖先以及该点到祖先的水平距离和竖直距离即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 40010
using namespace std;
struct data
{
int x , y , p , pos;
}q[10010];
int a[N] , b[N] , cx[N] , cy[N] , f[N] , dx[N] , dy[N] , ans[10010];
char str[5];
bool cmp(data a , data b)
{
return a.p < b.p;
}
int find(int x)
{
if(x == f[x]) return x;
int t = f[x];
f[x] = find(t);
dx[x] += dx[t];
dy[x] += dy[t];
return f[x];
}
int main()
{
int n , i , t , m , ta , tb;
scanf("%d%*d" , &n);
for(i = 1 ; i < n ; i ++ )
{
scanf("%d%d%d%s" , &a[i] , &b[i] , &t , str);
if(str[0] == 'E') cx[i] = t;
if(str[0] == 'S') cy[i] = t;
if(str[0] == 'W') cx[i] = -t;
if(str[0] == 'N') cy[i] = -t;
}
scanf("%d" , &m);
for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &q[i].x , &q[i].y , &q[i].p) , q[i].pos = i;;
sort(q + 1 , q + m + 1 , cmp);
for(i = 1 ; i <= n ; i ++ ) f[i] = i;
for(t = i = 1 ; i <= m ; i ++ )
{
while(t <= q[i].p)
{
ta = find(a[t]) , tb = find(b[t]);
if(ta != tb) f[ta] = tb , dx[ta] = dx[b[t]] + cx[t] - dx[a[t]] , dy[ta] = dy[b[t]] + cy[t] - dy[a[t]];
t ++ ;
}
ta = find(q[i].x) , tb = find(q[i].y);
ans[q[i].pos] = (ta == tb ? abs(dx[q[i].x] - dx[q[i].y]) + abs(dy[q[i].x] - dy[q[i].y]) : -1);
}
for(i = 1 ; i <= m ; i ++ ) printf("%d\n" , ans[i]);
return 0;
}
T2:求树的直径可以使用一遍树形dp,但这里懒了直接两遍dp求deep,即先找到任意一个deep最大的点,再以这个点为根求出直径。
#include <cstdio>
#include <cstring>
#define N 40010
int head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , deep[N];
void add(int x , int y , int z)
{
to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int fa)
{
int i;
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa)
deep[to[i]] = deep[x] + len[i] , dfs(to[i] , x);
}
int main()
{
int n , i , x , y , z , k;
scanf("%d%*d" , &n);
for(i = 1 ; i < n ; i ++ ) scanf("%d%d%d%*s" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
dfs(1 , 0);
for(k = 0 , i = 1 ; i <= n ; i ++ ) if(deep[i] > deep[k]) k = i;
memset(deep , 0 , sizeof(deep)) , dfs(k , 0);
for(k = 0 , i = 1 ; i <= n ; i ++ ) if(deep[i] > k) k = deep[i];
printf("%d\n" , k);
return 0;
}
T3:倍增LCA模板题,注意细节。
#include <cstdio>
#include <algorithm>
#define N 40010
using namespace std;
int head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , fa[N][18] , deep[N] , dis[N][18] , log[N];
void add(int x , int y , int z)
{
to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
int i;
for(i = 1 ; i <= log[deep[x]] ; i ++ )
fa[x][i] = fa[fa[x][i - 1]][i - 1] , dis[x][i] = dis[x][i - 1] + dis[fa[x][i - 1]][i - 1];
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x][0])
fa[to[i]][0] = x , dis[to[i]][0] = len[i] , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
int query(int x , int y)
{
int i , ans = 0;
if(deep[x] <= deep[y]) swap(x , y);
for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
if(deep[x] - (1 << i) >= deep[y])
ans += dis[x][i] , x = fa[x][i];
if(x == y) return ans;
for(i = log[deep[x]] ; ~i ; i -- )
if(fa[x][i] != fa[y][i])
ans += dis[x][i] + dis[y][i] , x = fa[x][i] , y = fa[y][i];
return ans + dis[x][0] + dis[y][0];
}
int main()
{
int n , i , x , y , z , k;
scanf("%d%*d" , &n);
for(i = 1 ; i < n ; i ++ ) scanf("%d%d%d%*s" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;
dfs(1);
scanf("%d" , &k);
while(k -- ) scanf("%d%d" , &x , &y) , printf("%d\n" , query(x , y));
return 0;
}
T4:树的点分治,几乎同 poj1741 ,还是单组测试数据,直接无视掉m和方向之后按照那道题做就好了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 40010
using namespace std;
int m , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , si[N] , deep[N] , root , vis[N] , f[N] , sn , d[N] , tot , ans;
void add(int x , int y , int z)
{
to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
}
void getroot(int x , int fa)
{
f[x] = 0 , si[x] = 1;
int i;
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa && !vis[to[i]])
getroot(to[i] , x) , si[x] += si[to[i]] , f[x] = max(f[x] , si[to[i]]);
f[x] = max(f[x] , sn - si[x]);
if(f[root] > f[x]) root = x;
}
void getdeep(int x , int fa)
{
d[++tot] = deep[x];
int i;
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa && !vis[to[i]])
deep[to[i]] = deep[x] + len[i] , getdeep(to[i] , x);
}
int calc(int x)
{
tot = 0 , getdeep(x , 0) , sort(d + 1 , d + tot + 1);
int i = 1 , j = tot , sum = 0;
while(i < j)
{
if(d[i] + d[j] <= m) sum += j - i , i ++ ;
else j -- ;
}
return sum;
}
void dfs(int x)
{
deep[x] = 0 , vis[x] = 1 , ans += calc(x);
int i;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]])
deep[to[i]] = len[i] , ans -= calc(to[i]) , sn = si[to[i]] , root = 0 , getroot(to[i] , 0) , dfs(root);
}
int main()
{
int n , i , x , y , z;
scanf("%d%*d" , &n);
for(i = 1 ; i < n ; i ++ )
scanf("%d%d%d%*s" , &x , &y , &z) , add(x , y , z) , add(y , x , z);
scanf("%d" , &m);
f[0] = 0x7fffffff , sn = n;
getroot(1 , 0) , dfs(root);
printf("%d\n" , ans);
return 0;
}
【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治的更多相关文章
- HDU 2545 树上战争 (并查集+YY)
题意:给一棵树,如果树上的某个节点被某个人占据,则它的所有儿子都被占据,lxh和pfz初始时分别站在两个节点上,lxh总是先移动 ,谁当前所在的点被另一个人占据,他就输了比赛,问谁能获胜 比较有意思的 ...
- hdu 2545 树上战争(并查集)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2545 树上战争 Time Limit: 10000/4000 MS (Java/Others) ...
- BZOJ 3365: [Usaco2004 Feb]Distance Statistics 路程统计
Description 一棵树,统计距离不大于 \(k\) 的点对个数. Sol 点分治. 发现自己快把点分治忘干净了... 找重心使所有儿子的最大值尽量小,然后每次处理全部子树,再减去每个子树的贡献 ...
- bzoj 3365 [Usaco2004 Feb]Distance Statistics 路程统计(点分治,单调)
[题意] 求树上长度不超过k的点对数目. [思路] 和 Tree 一样一样的. 就是最后统计的时候别忘把根加上. [代码] #include<set> #include<cmath& ...
- 【刷题】BZOJ 3365 [Usaco2004 Feb]Distance Statistics 路程统计
Description 在得知了自己农场的完整地图后(地图形式如前三题所述),约翰又有了新的问题.他提供 一个整数K(1≤K≤109),希望你输出有多少对农场之间的距离是不超过K的. Input 第1 ...
- bzoj 3365: [Usaco2004 Feb]Distance Statistics 路程统计【容斥原理+点分治】
统计在一个root下的两个子树,每个子树都和前面的运算一下再加进去对于这种需要排序的运算很麻烦,所以考虑先不去同子树内点对的算出合法点对个数,然后减去每一棵子树内的合法点对(它们实际上是不合法的,相当 ...
- USACO2004 cube stacking /// 带权并查集 oj1302
题目大意: 以N ( 1 ≤ N ≤ 30,000 )个堆栈开始,每个堆栈包含一个单独的立方体.执行P(1≤ P ≤100,000)的操作. 有两种类型的操作:移动和计数. *在移动操作中,将 包含方 ...
- BZOJ4699 树上的最短路(最短路径+dfs序+线段树+堆+并查集)
首先一般化的将下水道和塌陷看成一个东西.注意到在从源点出发的所有需要使用某条下水道的最短路径中,该下水道只会被使用一次,该下水道第一个被访问的点相同,且只会在第一个访问的点使用该下水道.这个第一个访问 ...
- 带权并查集【bzoj3362】: [Usaco2004 Feb]Navigation Nightmare 导航噩梦
[bzoj]3362: [Usaco2004 Feb]Navigation Nightmare 导航噩梦 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂 ...
随机推荐
- 【原创】os.chdir设置的工作路径和sys.path之间到底是个啥关系?
转载请注明出处:https://www.cnblogs.com/oceanicstar/p/9390455.html 直接放上测试后的结论(测试代码和截图过多,有兴趣的小伙伴可自己测试,未来看情况 ...
- solr6.6教程-core的添加(二)
1.什么是core core是solr的一个索引库,可以理解为一个数据库,core可以根据需要,创建多个. 2.创建core 首先进入到solrhome文件夹(D:\solrhome),创建一个文件夹 ...
- python核心编程2 第七章 练习
7-4. 建立字典. 给定两个长度相同的列表,比如说,列表[1, 2, 3,...]和['abc', 'def','ghi',...],用这两个列表里的所有数据组成一个字典,像这样:{1:'abc', ...
- vue.js 组件-全局组件和局部组件
这两天学习了Vue.js 感觉组件这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记. 首先Vue组件的使用有3个步骤,创建组件构造器,注册组件,使用组件3个方面. 代码演示如下: <! ...
- jquery点击按钮复制内容
做移动端的项目遇到一个需求要点击按钮复制dom里的内容,看了很多资料显示必须textarea或者input里的内容才能简单复制,还有就是用插件的了,最终都因为遇到各种问题放弃,最终选择了最简单的点击复 ...
- echarts饼图扇区添加点击事件
在echarts最后面添加上这段代码就可以了 function eConsole(param) { //alert(option.series[0].data.length); //alert(opt ...
- 【c学习-7】
#include /*#include"test31.c"*/ //定义阶乘函数 /* int fac(int n){ //定义寄存器存储变量 register int i ,f= ...
- Node.js中的不安全跳转如何防御详解
Node.js中的不安全跳转如何防御详解 导语: 早年在浏览器大战期间,有远见的Chrome认为要运行现代Web应用,浏览器必须有一个性能非常强劲的Java引擎,于是Google自己开发了一个高性能的 ...
- R语言绘图:ggplot2绘制ROC
使用ggplot2包绘制ROC曲线 rocplot<- function(pred, truth, ...){ predob<- prediction(pred, truth) #打印AU ...
- 为WPF中DropShadowBitmapEffect提供轻量级的替代品
原文:为WPF中DropShadowBitmapEffect提供轻量级的替代品 为WPF中DropShadowBitmapEffect提供轻量级的替代品 ...