A. Lorenzo Von Matterhorn

B.Minimum spanning tree for each edge

C.Misha, Grisha and Underground

D.Fools and Roads

E.City Driving

题意:给你一颗基环树(有n条边,n个点的连通图),有Q个询问,求u,v之间的距离最小是多少

思路:对于一棵基环树而言,只要去掉其环上任意一条边(a , b),其边权为w ,剩下的边就可以构成一棵树

   对于 u,v 之间的最小距离 , 有可能由去掉的边(a , b)构成 ,也有可能不需要边(a , b)

   不需要L的情况

      ans = dis(u , v)

   需要L的情况

      ans = min(dis(u , a) + dis(v , b) + w , dis(v , a) + dis(u , b) ,w)

   两种情况取min即可。

dfs序+RMQ做法(这个做法虽然有点麻烦,但是查询时O(1)的)

#include<cstdio>
#include<string.h>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cctype>
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define mem(a,x) memset(a,x,sizeof(a))
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid + 1,r
#define P pair<int,int>
#define ull unsigned long long
using namespace std;
typedef long long ll;
const int maxn = 1e5 + ;
const ll mod = 1e9 + ;
const int inf = 0x3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-;
int n;
int f[maxn];
int qx, qy, qv, cnt, num, si;
struct node
{
int y, v;
node(int a, int b)
{
y = a;
v = b;
}
};
vector<node>G[maxn];
int dp[][maxn * ], dis[maxn], vis[maxn], pos[maxn], res[maxn];
// res 存储欧拉序列 pos 存储每个节点第一次出现的位置 void init()
{
qx = qy = qv = ;
cnt = ;
num = ;
si = ;
mem(dp, );
mem(dis, );
mem(vis, );
mem(res, );
mem(pos, );
for (int i = ; i < maxn; ++i) G[i].clear();
} int find(int x)
{
return x == f[x] ? f[x] : f[x] = find(f[x]);
}
void bset(int x, int y)
{
int fx = find(x), fy = find(y);
f[fx] = fy;
}
void dfs(int u, int dist)
{
vis[u] = ;
dis[u] = dist;
pos[u] = cnt;
res[si] = u;
dp[][cnt++] = si++;
for (int i = ; i < G[u].size(); ++i)
{
int j = G[u][i].y;
if (!vis[j])
{
dfs(j, dist + G[u][i].v);
dp[][cnt++] = dp[][pos[u]];
}
}
} void RMQ()
{
for (int i = ; ( << i) <= n; ++i)
{
for (int j = n - ; j >= ; --j)
{
int k = ( << (i - ));
dp[i][j] = dp[i - ][j];
if (k + j < n)
{
dp[i][j] = min(dp[i][j], dp[i - ][j + k]);
}
}
}
} int cal(int i, int j)
{
if (i < j) swap(i, j);
int k = ;
while (( << k) <= (i - j + ))
++k;
--k;
k = min(dp[k][j], dp[k][i - ( << k) + ]);
return res[k];
} int Dis(int u, int v)
{
int k = cal(pos[u], pos[v]);
return dis[u] + dis[v] - dis[k] * ;
} int main()
{
while (scanf("%d", &n) != EOF)
{
if (n == ) break;
init();
for (int i = ; i <= n; ++i) f[i] = i;
for (int i = ; i <= n; ++i)
{
int x, y, v;
scanf("%d %d %d", &x, &y, &v);
x++, y++;
int fx = find(x), fy = find(y);
if (fx == fy)
{
qx = x, qy = y, qv = v;
continue;
}
bset(x, y);
G[x].push_back(node(y, v));
G[y].push_back(node(x, v));
}
for (int i = ; i <= n; ++i)
{
if (!vis[i])
dfs(i, );
}
n = n * - ;
RMQ();
int q;
scanf("%d", &q);
while (q--)
{
int x, y;
scanf("%d %d", &x, &y);
x++, y++;
int ans = Dis(x, y);
ans = min(ans, Dis(x, qx) + Dis(y, qy) + qv);
ans = min(ans, Dis(x, qy) + Dis(y, qx) + qv);
printf("%d\n", ans);
} }
return ;
} /* 7
0 1 2
0 2 3
1 3 2
2 3 8
2 4 3
3 5 1
1 6 7
3
4 5
0 6
1 2
0 */

F.Tree chain problem

题意:给你一颗由n个点构成的树,在给你m条链,让你选择一些不相交的链,使得权值最大

思路:用dp[i]记录以i为根节点的子树的权值最大值 , sum[i] 表示点 i 的所有儿子的dp值的和

   考虑到动态规划的无后效性,因此给我们的链我们尽在lca(u , v)处考虑拿或不拿(u , v  为其中一条链的端点)

   这一点的状态dp[i] ,仅有两种状况

      不取 lca(u , v) = i 的这条链

        dp[i] = sum[i]

      取 lca(u , v) = i 的这条链

        dp[i] = ( sum[i] + ∑  (sum[vi] - dp[vi]) + w )  vi 为链上的每一个节点

      因为需要取 lca(u , v) = i 的这条链 因此 vi 点不能取其他的链, 因为题目要求链之间不相交

    为了快速计算  ∑  (sum[vi] - dp[vi]) ,用 dfs序 和  树状数组 来维护 ∑  (sum[vi] - dp[vi])  的前缀和

    当我们求 ∑  (sum[vi] - dp[vi]) 只需要在 i 节点加入 树状数组前 查询 区间[1 , u]  + 区间[1 , v] 的和

#pragma comment(linker,"/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std; const int maxn = ;
const int maxb = ; struct Node {
int u, v, w;
Node(int u, int v, int w) :u(u), v(v), w(w) {}
}; int n, m;
vector<Node> que[maxn];
vector<int> G[maxn];
int lca[maxn][maxb], in[maxn], out[maxn], dep[maxn], dfs_cnt;
int sumv[maxn];// 树状数组
int dp[maxn], sum[maxn]; //计算dfs序,in,out;预处理每个顶点的祖先lca[i][j],表示i上面第2^j个祖先,lca[i][0]表示父亲
void dfs(int u, int fa, int d) {
in[u] = ++dfs_cnt; // 获取每个点进入的时间
lca[u][] = fa; dep[u] = d;
for (int j = ; j < maxb; j++) {
int f = lca[u][j - ];
lca[u][j] = lca[f][j - ];
}
for (int i = ; i < G[u].size(); i++) {
int v = G[u][i];
if (v == fa) continue;
dfs(v, u, d + );
}
out[u] = ++dfs_cnt; // 获取每个点出去的时间
} // 倍增法在线求lca ,o(n*logn)预处理+o(logn)询问
int Lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
//二进制倍增法,u,v提到相同高度
for (int i = maxb - ; i >= ; i--) {
if (dep[lca[u][i]] >= dep[v]) u = lca[u][i];
}
//当lca为u或者为v的时候
if (u == v) return u;
//lca不是u也不是v的情况
//一起往上提
for (int i = maxb - ; i >= ; i--) {
if (lca[u][i] != lca[v][i]) {
u = lca[u][i];
v = lca[v][i];
}
}
return lca[u][];
} //因为需要求出区间[in[u] , out[u]]上的(sum[i] - dp[i])的和 , 用树状数组维护 int get_sum(int x) {
int ret = ;
while (x > ) {
ret += sumv[x];
x -= x & (-x);
}
return ret;
} void add(int x, int v) {
while (x < maxn) {
sumv[x] += v;
x += x & (-x);
}
} //树形dp(用到dfs序和树状数组来快速计算链) void solve(int u, int fa) {
for (int i = ; i < G[u].size(); i++) {
int v = G[u][i];
if (v == fa) continue;
solve(v, u);
sum[u] += dp[v];
}
dp[u] = sum[u]; // 先将dp[u]处理为不选择以u为lca的链
for (int i = ; i < que[u].size(); i++) {
Node nd = que[u][i];
//get_sum(in[nd.u])处理的是lca(u,v)到u点这条路径的所有顶点
//get_sum(in[nd.v])处理的是lca(u,v)到v点这条路径的所有顶点
dp[u] = max(dp[u], sum[u] + get_sum(in[nd.u]) + get_sum(in[nd.v]) + nd.w);
}
add(in[u], sum[u] - dp[u]);
add(out[u], dp[u] - sum[u]);
} void init() {
dfs_cnt = ;
for (int i = ; i <= n; i++) G[i].clear();
for (int i = ; i <= n; i++) que[i].clear();
memset(lca, , sizeof(lca));
memset(sumv, , sizeof(sumv));
memset(sum, , sizeof(sum));
memset(dp, , sizeof(dp));
} int main() {
int tc;
scanf("%d", &tc);
while (tc--) {
scanf("%d%d", &n, &m);
init();
for (int i = ; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(, , );
while (m--) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
//每条链在Lca的位置上处理,这样符合dp的无后效性
que[Lca(u, v)].push_back(Node(u, v, w));
}
solve(, );
printf("%d\n", dp[]);
}
for (int i = ; i <= n; ++i)
{
printf("%d ", dp[i]);
}
printf("\n");
for (int i = ; i <= n; ++i)
{
printf("%d ", sum[i]);
}
printf("\n");
return ;
}

1月18日 LCA专项训练的更多相关文章

  1. 深度学习DeepLearning技术实战(12月18日---21日)

    12月线上课程报名中 深度学习DeepLearning(Python)实战培训班 时间地点: 2020 年 12 月 18 日-2020 年 12 月 21日 (第一天报到 授课三天:提前环境部署 电 ...

  2. 2016年12月18日 星期日 --出埃及记 Exodus 21:13

    2016年12月18日 星期日 --出埃及记 Exodus 21:13 However, if he does not do it intentionally, but God lets it hap ...

  3. 2015年8月18日,杨学明老师《技术部门的绩效管理提升(研讨会)》在中国科学院下属机构CNNIC成功举办!

    2015年8月18日,杨学明老师为中国网络新闻办公室直属央企中国互联网络中心(CNNIC)提供了一天的<技术部门的绩效管理提升(研讨会)>培训课程.杨学明老师分别从研发绩效管理概述.研发绩 ...

  4. 2016年11月18日 星期五 --出埃及记 Exodus 20:9

    2016年11月18日 星期五 --出埃及记 Exodus 20:9 Six days you shall labor and do all your work,六日要劳碌作你一切的工,

  5. 2016年10月18日 星期二 --出埃及记 Exodus 19:2

    2016年10月18日 星期二 --出埃及记 Exodus 19:2 After they set out from Rephidim, they entered the Desert of Sina ...

  6. 天津Uber优步司机奖励政策(1月18日~1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. 长沙Uber优步司机奖励政策(1月18日~1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  8. 西安Uber优步司机奖励政策(1月18日~1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. 上海Uber优步司机奖励政策(1月18日~1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

随机推荐

  1. 从零到Django大牛的的进阶之路01

    搭建 创建虚拟环境 mkvirtualenv django_py3_1.11 -p python3 安装Django pip install django==1.11.11 创建工程 django-a ...

  2. Golang的运算符-算数运算符

    Golang的运算符-算数运算符 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.算术运算符概述 常见的算术运算符: +: 可表示正号,如",结果为"Jaso ...

  3. GNS3 模拟icmp端口不可达

    R1 : conf t int f0/0 no shutdown ip add 192.168.1.1 255.255.255.0 no ip routing end R2 f0/0: conf t ...

  4. Python基础笔记:函数:调用函数、定义函数、函数的参数、递归函数

    一.定义一个求二元一次方程的根的函数 #Sublime Text import math def ee(a,b,c): delta=b*b-4*a*c if delta<0: return 'n ...

  5. golang用slice、list当queue

    背景 golang没有queue这种类型,不过可以用slice.list模拟 slice当queue 声明队列 var queue []int 入队一个元素: queue = append(queue ...

  6. 124-PHP类析构函数

    <?php class myclass{ //定义一个类 public function __destruct(){ //定义析构方法 echo '析构方法执行.<br />'; } ...

  7. JS给对象添加新字段

    //这是我的对象 var myInfo = { "name":"peng ya ping", "tel":"138888888&q ...

  8. ACM-Work Assignment

    题目描述:Work Assignment   设有n件工作分配给n个人.将工作i 分配给第j 个人所需的费用为Cij.试设计一个算法,为每一个人都分配1 件不同的工作,并使总费用达到最小. 设计一个算 ...

  9. Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) E. Arson In Berland Forest

    E. Arson In Berland Forest The Berland Forest can be represented as an infinite cell plane. Every ce ...

  10. C++ 管道

    // PipeServer.cpp : Defines the entry point for the console application. // #include "stdafx.h& ...