以1为根建树,先将所有路径挂在lca上,再分两类讨论:

1.lca相同,此时我们仅关心于lca上不经过第$a$和$b$个儿子路径数,容斥一下,即所有路径-经过$a$的-经过$b$的+经过$a$和$b$的,前三个很容易统计,最后一个用map即可

(这样分类主要是避免lca相同时重复计数)

2.lca不同,考虑其中lca深度较小的路径,将这条路径分为不包含lca的两段,那么另外一条路径的lca一定恰好在其中一条路径上

更具体的,由于两段对称,仅考虑其中一段,如果暴力统计,也就是枚举这段上的每一个节点,假设先枚举的路径是第$a$个儿子,也就是求该点上所有路径-经过$a$的路径

(特别的,对于叶子要将所有以该点为lca的路径全部累计)

不难发现这件事情可以差分,即对于每一个点,记录”其到根路径通过上述方法得出的答案“,转移通过父亲求出,之后差分即可

由于求lca还需要倍增,然后还有map,总复杂度为$o(n\log n)$

  1 #include<bits/stdc++.h>
2 using namespace std;
3 #define N 300005
4 #define mp make_pair
5 struct Edge{
6 int nex,to;
7 }edge[N<<1];
8 struct path{
9 int x,y,x0,y0;
10 }a[N];
11 vector<int>v[N];
12 map<int,int>mat[N];
13 int E,n,m,x,y,head[N],dep[N],id[N],sum[N],dp[N],f[N][21];
14 long long ans;
15 void add(int x,int y){
16 edge[E].nex=head[x];
17 edge[E].to=y;
18 head[x]=E++;
19 }
20 int lca(int x,int y){
21 if (dep[x]<dep[y])swap(x,y);
22 for(int i=20;i>=0;i--)
23 if (dep[f[x][i]]>=dep[y])x=f[x][i];
24 if (x==y)return x;
25 for(int i=20;i>=0;i--)
26 if (f[x][i]!=f[y][i]){
27 x=f[x][i];
28 y=f[y][i];
29 }
30 return f[x][0];
31 }
32 int get(int x,int y){
33 if (x==y)return x;
34 for(int i=20;i>=0;i--)
35 if (dep[f[x][i]]>dep[y])x=f[x][i];
36 return x;
37 }
38 void dfs(int k,int fa,int s){
39 dep[k]=s;
40 f[k][0]=fa;
41 for(int i=1;i<=20;i++)f[k][i]=f[f[k][i-1]][i-1];
42 for(int i=head[k];i!=-1;i=edge[i].nex)
43 if (edge[i].to!=fa)dfs(edge[i].to,k,s+1);
44 }
45 void calc(int k,int fa){
46 id[0]=id[k]=0;
47 for(int i=head[k];i!=-1;i=edge[i].nex)
48 if (edge[i].to!=fa)id[edge[i].to]=++id[0];
49 for(int i=0;i<v[k].size();i++)
50 if (id[a[v[k][i]].x0]>id[a[v[k][i]].y0]){
51 swap(a[v[k][i]].x,a[v[k][i]].y);
52 swap(a[v[k][i]].x0,a[v[k][i]].y0);
53 }
54 for(int i=0;i<=id[0];i++){
55 sum[i]=0;
56 mat[i].clear();
57 }
58 for(int i=0;i<v[k].size();i++){
59 sum[id[a[v[k][i]].x0]]++;
60 sum[id[a[v[k][i]].y0]]++;
61 mat[id[a[v[k][i]].x0]][id[a[v[k][i]].y0]]++;
62 }
63 for(int i=0;i<v[k].size();i++)
64 if (!id[a[v[k][i]].x0]){
65 if (!id[a[v[k][i]].y0])ans+=(int)v[k].size()-1;
66 else ans+=(int)v[k].size()-sum[id[a[v[k][i]].y0]];
67 }
68 else{
69 ans+=(int)v[k].size()-sum[id[a[v[k][i]].x0]]-sum[id[a[v[k][i]].y0]];
70 ans+=mat[id[a[v[k][i]].x0]][id[a[v[k][i]].y0]];
71 }
72 for(int i=head[k];i!=-1;i=edge[i].nex)
73 if (edge[i].to!=fa)dp[edge[i].to]=dp[k]+(int)v[k].size()-sum[id[edge[i].to]];
74 for(int i=head[k];i!=-1;i=edge[i].nex)
75 if (edge[i].to!=fa)calc(edge[i].to,k);
76 }
77 int main(){
78 scanf("%d",&n);
79 memset(head,-1,sizeof(head));
80 for(int i=1;i<n;i++){
81 scanf("%d%d",&x,&y);
82 add(x,y);
83 add(y,x);
84 }
85 dfs(1,1,0);
86 scanf("%d",&m);
87 for(int i=1;i<=m;i++){
88 scanf("%d%d",&a[i].x,&a[i].y);
89 int z=lca(a[i].x,a[i].y);
90 a[i].x0=get(a[i].x,z);
91 a[i].y0=get(a[i].y,z);
92 v[z].push_back(i);
93 }
94 calc(1,0);
95 ans/=2;
96 for(int i=1;i<=m;i++){
97 int z=lca(a[i].x,a[i].y);
98 if (a[i].x!=z)ans+=dp[a[i].x]-dp[a[i].x0]+(int)v[a[i].x].size();
99 if (a[i].y!=z)ans+=dp[a[i].y]-dp[a[i].y0]+(int)v[a[i].y].size();
100 }
101 printf("%lld",ans);
102 }

[cf1486F]Pairs of Paths的更多相关文章

  1. 【SSSP】A forward-backward single-source paths algorithm

    0. 引子基础的算法和数据结构已经学习的差不多了,上学期期末就打算重点研究研究STOC和FOCS上面的论文.做这件事情的初衷是了解别人是如何改进原有算法的,搞清楚目前比较热的算法问题有哪些,更重要的是 ...

  2. 【LeetCode OJ】Word Ladder II

    Problem Link: http://oj.leetcode.com/problems/word-ladder-ii/ Basically, this problem is same to Wor ...

  3. Floyd算法(弗洛伊德算法)

    算法描述: Floyd算法又称为弗洛伊德算法,插点法,是一种用于寻找给定的加权图中顶点间最短路径的算法.从图的带权邻接矩阵A=[a(i,j)] n×n开始,递归地进行n次更新,即由矩阵D(0)=A,按 ...

  4. C++编程练习(11)----“图的最短路径问题“(Dijkstra算法、Floyd算法)

    1.Dijkstra算法 求一个顶点到其它所有顶点的最短路径,是一种按路径长度递增的次序产生最短路径的算法. 算法思想: 按路径长度递增次序产生算法: 把顶点集合V分成两组: (1)S:已求出的顶点的 ...

  5. Graph图总结

    将COMP20003中关于Graph的内容进行总结,内容来自COMP20003,中文术语并不准确,以英文为准. Graph G = {V, E} 顶Vertices V: can contain in ...

  6. 【推荐】Data Structure Visualizations

    University of San Francisco    David Galles 功能:可视化数据结构&算法实现过程 网站地址  https://www.cs.usfca.edu/~ga ...

  7. Floyd最短路(带路径输出)

    摘要(以下内容来自百度) Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似. 该算法名称以创始人之一.1978年图灵奖获得者. ...

  8. SOAPdenove 使用

    0. 该软件原理 它以kerm为节点单位,利用de Bruijn图的方法实现全基因组的组装.何为de Bruijn............... contig 的构建过程: (1)选取初始Kmer, ...

  9. Floyd算法(弗洛伊德算法) 百度百科

    核心代码 for(int k=1; k<=NODE; ++k)//对于每一个中转点 for(int i=0; i<=NODE; ++i)//枚举源点 for(int j=0; j<= ...

随机推荐

  1. 手把手教你 Docker部署可视化工具Grafana

    一.Grafana的简单介绍 Grafana是开源的.炫酷的可视化监控.分析利器,无论您的数据在哪里,或者它所处的数据库是什么类型,您都可以将它与Grafana精美地结合在一起.它还有丰富的套件供您选 ...

  2. 1-Java继承中多态情况特性下变量,方法,静态方法的访问

    在Java继承下,多态特性下类成员访问情况 /* 在继承中,变量时静态的绑定的,非静态方法是动态的绑定的,静态方法是静态绑定的 */ class Parent{ int number = 11; pu ...

  3. 看动画学算法之:平衡二叉搜索树AVL Tree

    目录 简介 AVL的特性 AVL的构建 AVL的搜索 AVL的插入 AVL的删除 简介 平衡二叉搜索树是一种特殊的二叉搜索树.为什么会有平衡二叉搜索树呢? 考虑一下二叉搜索树的特殊情况,如果一个二叉搜 ...

  4. Java基础之(九):循环结构

    循环结构 前面我们已经聊过了if语句,接下来我们就聊聊跟选择语句一样常用的语句:循环语句. 同样的,所有的循环语句都可以写成for语句 while循环 while循环的格式: while(布尔表达式) ...

  5. Min_25筛 学习小记

    前言 为什么叫学习小记呢?因为暂时除了模板题就没有做其他的东西了.(雾 这个东西折磨了我一整天,看得我身不如死,只好结合代码理解题解,差点死在机房.(话说半天综合半天竞赛真是害人不浅) 为了以后忘了再 ...

  6. 02Prism WPF 入门实战 - 建项

    1.概要 Prism介绍 Github: https://github.com/PrismLibrary/Prism 开发文档:https://prismlibrary.com/docs/ Prism ...

  7. ClickHouse 存算分离架构探索

    背景 ClickHouse 作为开源 OLAP 引擎,因其出色的性能表现在大数据生态中得到了广泛的应用.区别于 Hadoop 生态组件通常依赖 HDFS 作为底层的数据存储,ClickHouse 使用 ...

  8. Win10 配置JDK1.8 (JDK 8)环境变量

    JDK的安装: 1. JDK安装过程中,一般X掉公共JRE,因为JDK包含了JRE:     环境变量的配置: 1. 打开环境变量,编辑系统变量,新建: 变量名:JAVA_HOME 变量值:D:\so ...

  9. k8s replicaset controller分析(1)-初始化与启动分析

    replicaset controller分析 replicaset controller简介 replicaset controller是kube-controller-manager组件中众多控制 ...

  10. 大厂面试题系列:重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分

    面试题:重载(Overload)和重写(Override)的区别.重载的方法能否根据返回类型进行区分 面试官考察点猜想 这道题纯粹只是考查基础理论知识,对实际开发工作中没有太多的指导意义,毕竟编辑器都 ...