hihoCoder挑战赛11.题目4 : 高等理论计算机科学(LCA)
clj在某场hihoCoder比赛中的一道题,表示clj的数学题实在6,这道图论貌似还算可以。。。
题目链接:http://hihocoder.com/problemset/problem/1167
由于是中文题目,题意不再赘述。
对于任意两条小精灵的活动路径a和b,二者相交的判断条件为b的两个端点的LCA在a的路径上;那么我们可以首先将每个活动路径端点的LCA离线预处理出来,对每个节点LCA值+1。
然后以某个节点(我选择的是节点1)为根进行深搜,算出一条从节点1到节点x的LCA值和,那么任意路径a(假设其两端点分别是A和B)上的节点个数就是sum[A] + sum[B] - 2 * sum[LCA(A,B)]。
最后,对于某些点,如果它是不止一条路径的LCA,那么我们只需要对最终答案乘以C(LCAnum, 2)的组合数就好。
【PS:clj给出的题解中,采用了点分治+LCA的方式,虽然看懂了题意,但是表示对递归分治之后的路径,如何求出其上的LCAnum,并没有多好的想法,还望巨巨能指点一下,Thx~】
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
#define MAXN 100010
struct Edge {
int to, next;
} edge[MAXN << ];
struct Node {
int to, next, num;
} Query[MAXN << ];
struct node {
int u, v, lca;
} input[MAXN];
int totEdge, totQuery, n, m;
int headEdge[MAXN], headQuery[MAXN];
int ancestor[MAXN], father[MAXN], LCAnum[MAXN], sum[MAXN];
bool vis[MAXN];
void addEdge(int from, int to) {
edge[totEdge].to = to;
edge[totEdge].next = headEdge[from];
headEdge[from] = totEdge++;
}
void addQuery(int from, int to, int x) {
Query[totQuery].to = to;
Query[totQuery].num = x;
Query[totQuery].next = headQuery[from];
headQuery[from] = totQuery++;
}
void init() {
memset(headEdge, -, sizeof(headEdge));
memset(headQuery, -, sizeof(headQuery));
memset(father, -, sizeof(father));
memset(vis, false, sizeof(vis));
memset(sum, , sizeof(sum));
memset(LCAnum, , sizeof(LCAnum));
totEdge = totQuery = ;
}
int find_set(int x) {
if(x == father[x]) return x;
else return father[x] = find_set(father[x]);
}
void union_set(int x, int y) {
x = find_set(x); y = find_set(y);
if(x != y) father[y] = x;
}
void Tarjan(int u) {
father[u] = u;
for(int i = headEdge[u]; i != -; i = edge[i].next) {
int v = edge[i].to;
if(father[v] != -) continue;
Tarjan(v);
union_set(u, v);
}
for(int i = headQuery[u]; i != -; i = Query[i].next) {
int v = Query[i].to;
if(father[v] == -) continue;
input[Query[i].num].lca = find_set(v);
}
}
void DFS(int u, int pre) {
vis[u] = ;
sum[u] = sum[pre] + LCAnum[u];
for(int i = headEdge[u]; i != -; i = edge[i].next) {
int v = edge[i].to;
if(vis[v]) continue;
DFS(v, u);
}
}
int main() {
init();
scanf("%d%d", &n, &m);
for(int i = ; i < n - ; i++) {
int a, b;
scanf("%d%d", &a, &b);
addEdge(a, b); addEdge(b, a);
}
for(int i = ; i < m; i++) {
int a, b;
scanf("%d%d", &a, &b);
input[i].u = a, input[i].v = b;
addQuery(a, b, i); addQuery(b, a, i);
}
Tarjan();
for(int i = ; i < m; i++)
LCAnum[input[i].lca]++;
DFS(, );
LL ans = ;
for(int i = ; i < m; i++) {
ans += (sum[input[i].u] + sum[input[i].v] - * sum[input[i].lca]);
}
for(int i = ; i <= n; i++) {
ans += (LL)LCAnum[i] * (LCAnum[i] - ) / ;
}
printf("%lld\n", ans);
return ;
}
转载:)
hihoCoder挑战赛11.题目4 : 高等理论计算机科学(LCA)的更多相关文章
- 【Hihocoder 1167】 高等理论计算机科学 (树链的交,线段树或树状数组维护区间和)
[题意] 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 少女幽香这几天正在学习高等理论计算机科学,然而她什么也没有学会,非常痛苦.所以她出去晃了一晃,做起了一些没什么意 ...
- [题解]hihoCoder挑战赛18——题目1 神奇字符串
题目地址:http://hihocoder.com/problemset/problem/1264 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 我们说两个字符串是非常 ...
- hihoCoder挑战赛28 题目3 : 树的方差
题目3 : 树的方差 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 对于一棵 n 个点的带标号无根树,设 d[i] 为点 i 的度数. 定义一棵树的方差为数组 d[1. ...
- hihoCoder挑战赛28 题目2 : 二进制翻转
题目2 : 二进制翻转 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 定义函数 Rev(x) 表示把 x 在二进制表示下翻转后的值 例如: Rev(4)=1,因为 4 ...
- hihoCoder挑战赛28 题目1 : 异或排序
题目1 : 异或排序 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个长度为 n 的非负整数序列 a[1..n] 你需要求有多少个非负整数 S 满足以下两个条件: ...
- hihoCoder挑战赛27题目一 福字 (dp)
题目: 一个n × n的矩阵,其中每个位置都是一个非负整数. 一个福字被定义成是大小为 k 的正方形,满足其中的每个位置上的数都恰好比他的左边的那个和上边的那个大1(如果左边或上边的那个不存在的话就无 ...
- hihoCoder挑战赛11 A 随机斐波那契
算了前三项.....发现是个大水题... #include<stdio.h> int main() { int n; while (~scanf("%d", &am ...
- 【hihocoder1167】高等理论计算机科学 (重链剖分 +树状数组)
Descroption 原题链接给你一棵\(~n~\)个点的树和\(~m~\)条链,求两两相交的链有多少对,两条链相交当且仅当有至少一个公共点.\(~1 \leq n, m \leq 10 ^ 5~\ ...
- hihoCoder挑战赛23
hihoCoder挑战赛23 A.Emulator 题意 给一张图,有\(N(N \le 300)\)个点, 给出任意两点之间的最短路. 求最多可以去掉多少条边,使得任意两点的最短路长度不变. 思路 ...
随机推荐
- PriorityQueue
基本概念 顾名思义,PriorityQueue是优先级队列,它首先实现了队列接口(Queue),与LinkedList类似,它的队列长度也没有限制,与一般队列的区别是,它有优先级的概念,每个元素都有优 ...
- eclipse+SVN文件只显示版本号,不显示时间和作者解决办法
SVN默认是显示提交次数的 改成这样 就可以了...
- [Android]关于Activity的InstanceState
Activity有两个方法onSaveInstanceState() 和 onRestoreInstanceState(). onSaveInstanceState()方法只适合用于保存一些临时性的状 ...
- 创建与合并分支-git入门教程
在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支.截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支.HEAD严格来说不是指向提交,而 ...
- 深入JVM-垃圾回收概念与算法
一.认识垃圾回收 谈到垃圾回收(Garbage Collection,简称GC),GC中的垃圾,特指存在于内存中的.不会再被使用的对象.对于内存空间的管理来说,识别和清理垃圾对象是至关重要的. 二.常 ...
- iOS qrcode 默认尺寸与修改
四种容错格式的尺寸:27.31.31.35. // 5.将CIImage转换成UIImage,并放大显示 UIImage *imagex = [UIImage imageWithCIImage:out ...
- 命名实参和可选实参 Named and Optional Arguments
1. 利用“命名实参”,您将能够为特定形参指定实参,方法是将实参与该形参的名称关联,而不是与形参在形参列表中的位置关联. static void Main(string[] args) { Conso ...
- git config --global core.autocrlf false
git config --global core.autocrlf false warning: LF will be replaced by CRLF in .idea/vcs.xml.The f ...
- MyISAM 和InnoDB 区别 转
MyISAM 和InnoDB 讲解 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定.基本的差别为:MyISAM类型不支持事务处理等高级处理 ...
- Runner站立会议01
开会时间:9.10-9.30 地点:二教 今天做了什么:学了文本输入,button按钮,界面转换(意图) 遇到什么困难:界面转换时,出现问题,没有正常跳转 明天打所作什么:解决今天遇到的问题,学了第二 ...