(树形dp+LCA倍增法)CSU 1915 - John and his farm

题意:

有一个棵树,现在让你找两个点连接起来,这样必然成为一个环,现在要求这些环长度的期望,也就是平均值。

分析:

第一次做LCA题,做多校的时候,瞎几把找了模板敲,敲了个八九不离十,只是姿势不太好,需要考虑很多细节。

其实我觉得这题最多只能算中等题。

因为一直没空,写题解也晚了,已经有很多人写了题解,都写的不错。反正比我厉害。

这题用倍增法比较好一些,因为会用到关键点,也就是当v和u处在同一棵子树中时,找到更高点的下面那个点,倍增法通过深度跳跃可以很快找到。处理起来比其他两个LCA算法都方便。

代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <iostream>
  5 #include <vector>
  6 #include <map>
  7 #include <queue>
  8
  9
 10 using namespace std;
 11
 12 const int inf = 0x3f3f3f3f;
 13 const int maxn = 100010;
 14 const int DEG = 20;
 15
 16 #define fi first
 17 #define se second
 18
 19 typedef long long ll;
 20
 21 vector<int>  g[maxn];
 22
 23 int fa[maxn][DEG];
 24 int deg[maxn];
 25
 26 int n, q;
 27 int u, v;
 28
 29 void bfs(int root) {
 30     queue<int> q;
 31     deg[root] = 0;
 32     fa[root][0] = root;
 33     q.push(root);
 34     while(!q.empty()) {
 35         int tmp = q.front();
 36         q.pop();
 37         for(int i = 1; i < DEG; i++) {
 38             fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
 39         }
 40         for(int i = 0; i < g[tmp].size(); i++) {
 41             int v = g[tmp][i];
 42             if(v == fa[tmp][0])continue;
 43             deg[v] = deg[tmp] + 1;
 44             fa[v][0] = tmp;
 45             q.push(v);
 46         }
 47     }
 48 }
 49
 50 int LCA(int u, int v) {
 51     if(deg[u] > deg[v])swap(u, v);
 52     for(int det = deg[v] - deg[u], i = 0; det; det >>= 1, i++) {
 53         if(det & 1)v = fa[v][i];
 54     }
 55     if(u == v)return u;
 56     for(int i = DEG - 1; i >= 0; i--) {
 57         if(fa[u][i] != fa[v][i]) {
 58             u = fa[u][i];
 59             v = fa[v][i];
 60         }
 61     }
 62     return fa[u][0];
 63 }
 64
 65 int len[maxn][2];
 66 int son[maxn];
 67
 68 void dfs1(int u, int pre) {
 69     son[u] = 1;
 70     for(int i = 0; i < g[u].size(); i++) {
 71         int v = g[u][i];
 72         if(v == pre)continue;
 73         dfs1(v, u);
 74         son[u] += son[v];
 75         len[u][0] += len[v][0] + son[v];
 76     }
 77     len[u][1] = len[u][0];
 78 }
 79
 80 void dfs2(int u, int pre) {
 81     for(int i = 0; i < g[u].size(); i++) {
 82         int v = g[u][i];
 83         if(v == pre)continue;
 84         len[v][1] += (len[u][1] - len[v][0] - son[v] + n - son[v]);
 85         dfs2(v, u);
 86     }
 87 }
 88
 89 int up(int x, int step) {
 90     for(int i = 0; i < DEG; i++) {
 91         if(step & (1 << i))x = fa[x][i];
 92     }
 93     return x;
 94 }
 95
 96 void init() {
 97     memset(len, 0, sizeof(len));
 98     memset(son, 0, sizeof(son));
 99     for(int i = 0; i < maxn; i++)g[i].clear();
100 }
101
102
103
104
105 int main() {
106     while(~scanf("%d%d", &n, &q)) {
107         init();
108         for(int i = 0; i < n - 1; i++) {
109             scanf("%d%d", &u, &v);
110             g[u].push_back(v);
111             g[v].push_back(u);
112         }
113         bfs(1);
114         dfs1(1, 1);
115         dfs2(1, 1);
116
117         while(q--) {
118             scanf("%d%d", &u, &v);
119             if(deg[u] < deg[v])swap(u, v);
120             int f = LCA(u, v);
121             int dis = deg[u] + deg[v] - deg[f] * 2;
122             if(f == v) {
123                 int core = up(u, deg[u] - deg[v] - 1);
124                 ll sum = (ll)son[u] * (n - son[core]);
125                 ll tolen = (ll)len[u][0] * (n - son[core]) + (ll)(len[v][1] - len[core][0] - son[core]) * son[u] + (ll)dis * sum + sum;
126                 printf("%f\n", tolen * 1.0 / sum);
127             } else {
128                 ll sum = (ll)son[v] * son[u];
129                 ll tolen = (ll)len[v][0] * son[u] + (ll)len[u][0] * son[v] + (ll)sum * dis + sum;
130                 printf("%f\n", tolen * 1.0 / sum);
131             }
132
133
134         }
135
136     }
137     return 0;
138
139 }
时间: 2024-11-03 22:15:44

(树形dp+LCA倍增法)CSU 1915 - John and his farm的相关文章

【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树形dp/LCA/树的点分治

题目描述 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M(2≤M≤40000)条的不同的垂直或水平的道路连结着农场,道路的长度不超过1000.这些农场的分布就像下面的地图一样, 图中农场用F1..F7表示, 每个农场最多能在东西南北四个方向连结4个不同的农场.此外,农场只处在道路的两端.道路不会交叉且每对农场间有且仅有一条路径.邻居鲍伯要约翰来导航,但约翰丢了农场的地图,他只得从电脑的备份中修复了.每一条道路的信息如下: 从农场23往南经距离10到达农场17 从农场1往东经距离7到达农

HDU 5296 Annoying Problem 树链剖分 LCA 倍增法

HDU 5296 Annoying Problem 题目链接:hdu 5296 题意:在一棵给定的具有边权的树,一个节点的集合S(初始为空),给定Q个操作,每个操作增加或删除S中的一个点,每个操作之后输出使集合S中所有点联通的最小子树的边权和. 思路:最小子树上的节点的充要条件: 节点为(S集合中所有点的LCA)的子节点: 节点有一个子孙为S集合中的点. 那么我们给每个节点都开一个标记数组,初始为零,每加入一个节点,就把从这个节点到根节点路径上的点的值都+1,反之-1,这样通过对每个单节点值的查

poj1470 LCA倍增法

倍增法模板题 #include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; #define maxn 1000 #define DEG 20 struct Edge{ int to,next; }edge[maxn*maxn*2]; int head[maxn],tot; void addedge(int u,int v){ edge[to

HDU4008 Parent and son(树形DP LCA)

先记录以1为根时每个节点子树儿子节点的最大与次小值,询问x, y时,先判断x在不在y的子树范围内,若不在,结果为y的儿子结点,后继的最小值. 若x在y的子树范围内,若y儿子最小值是x的前驱,从次小值与父亲节点转移,否则从最小值与父亲节点转移. #include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<string> #include<al

LCA(倍增在线算法) codevs 2370 小机房的树

codevs 2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力.已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计

codeforces 587C:(LCA倍增+维护最小值)

一开始直接无脑tarjan,回溯只能一层层往上走,太慢了,加了各种优化还是TLE 后来了解到LCA倍增法(在线).复杂度其实相比LCA转RMQ以及tarjan是要稍差一些,但是其中能同步维护的只有LCA倍增,很神奇的算法 #include"cstdio" #include"queue" #include"cmath" #include"stack" #include"iostream" #include&q

UVa 11354 Bond 最小生成树+LCA倍增

题目来源:UVa 11354 Bond 题意:n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 思路:使最大的危险系数尽量小 答案是最小生成树上的边 然后用LCA倍增法记录s和t到他们最近公共祖先的最大值 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 50010; const int INF =

树形DP+(分组背包||二叉树,一般树,森林之间的转换)codevs 1378 选课

codevs 1378 选课 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的.学生选修了这M门课并考核通过就能获得相应的学分. 在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修.例如<Frontpage>必须在

Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]

题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_dis[i],维护非子树节点中距离该点的最大值fa_dis[i]; 2.对于每个节点维护它最大的三个儿子节点的son_dis; 3.维护up[i][j]和down[i][j]数组,这个类似倍增lca里边的fa[i][j],up[i][j]代表的含义是从第j个点向上到它的第2^i个父节点这条链上的点除了该