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. python用于web题里写解密脚本

    题源自bugku里的WEB3 选择选项让他停止,F12后出现如下代码,一看数字就知道是ASC: 复制出来,写pyhton脚本如下,在编译器里跑一下 s='KEY{J2sa42ahJK-HS11III} ...

  2. 对上一篇Logstash的补充

    主要补充内容: 1.同步多表 2.配置的参数个别说明 3.elasticsearch的"_id"如果有相同的,那么会覆盖掉,相同"_id"的数据只会剩下最后一条 ...

  3. EditText标签的使用

    前文: 介绍EditText的使用,这个是文本输入控件,用来输入文本内容 使用: EditText继承TextView所以TextView的东西EditText都可以使用 text:显示文本 text ...

  4. HDU - 6006 Engineer Assignment (状压dfs)

    题意:n个工作,m个人完成,每个工作有ci个阶段,一个人只能选择一种工作完成,可以不选,且只能完成该工作中与自身标号相同的工作阶段,问最多能完成几种工作. 分析: 1.如果一个工作中的某个工作阶段没有 ...

  5. apache启动错误:Could not reliably determine the server's fully qualified domain name

    启动apache遇到错误:httpd: Could not reliably determine the server's fully qualified domain name [root@serv ...

  6. VS Code 单文件、多文件(工程) 配置文件

    针对于单文件编译运行,需要在代码文件夹下建立子文件夹 .vscode ,并放置三个文件 1:c_cpp_properties.json,注意更改7.8.11行的路径 { "configura ...

  7. Django——优美的Path()函数

    path( )作用:解析URL地址 path( ) 标准语法: (<>为必须的参数,[]为可选参数) path(<route>, <view>, [name=Non ...

  8. 134-PHP子类重写父类方法,并调用父类方法

    <?php class father{ //定义father类 public function method(){ //定义方法 echo '<br />father method' ...

  9. hdu 3308 线段树,单点更新 求最长连续上升序列长度

    LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  10. (二)requests模块

    一 requests模块 概念: python中原生的基于网络请求的模块,模拟浏览器进行请求发送,获取页面数据 安装: pip install requests 二 requests使用的步骤 1 指 ...