【BZOJ-3910】火车 倍增LCA + 并查集

3910: 火车

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 262  Solved: 90
[Submit][Status][Discuss]

Description

A 国有n 个城市,城市之间有一些双向道路相连,并且城市两两之间有唯一路径。现在有火车在城市 a,需要经过m 个城市。火车按照以下规则行驶:每次行驶到还没有经过的城市中在 m 个城市中最靠前的。现在小 A 想知道火车经过这m 个城市后所经过的道路数量。

Input

第一行三个整数 n、m、a,表示城市数量、需要经过的城市数量,火车开始时所在位置。

接下来 n-1 行,每行两个整数 x和y,表示 x 和y之间有一条双向道路。

接下来一行 m 个整数,表示需要经过的城市。

Output

一行一个整数,表示火车经过的道路数量。

Sample Input

5 4 2
1 2
2 3
3 4
4 5
4 3 1 5

Sample Output

9

HINT

N<=500000 ,M<=400000

Source

Solution

水题- -最多算个并查集的有趣应用

很显然直接询问用LCA统计答案即可

至于处理走过的路径,拿并查集维护一下,很简单的把起止点到LCA的点合并一下,询问的两个点如果属于一个集合显然走过

Code

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 501000
int n,m,a;
struct EdgeNode{int to,next;}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void insert(int u,int v) {add(u,v); add(v,u);}
int deep[maxn],father[maxn][20],ffff[maxn];
long long ans;
void dfs(int now)
{
    for (int i=1; i<=19; i++)
        if (deep[now]>=(1<<i))
            father[now][i]=father[father[now][i-1]][i-1];
        else
            break;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=father[now][0])
            {
                deep[edge[i].to]=deep[now]+1;
                father[edge[i].to][0]=now;
                dfs(edge[i].to);
            }
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; (1<<i)<=dd; i++)
        if (dd&(1<<i)) x=father[x][i];
    for (int i=19; i>=0; i--)
        if (father[x][i]!=father[y][i])
            x=father[x][i],y=father[y][i];
    if (x==y) return x;
    return father[x][0];
}
int find(int x) {if (ffff[x]==x) return x; ffff[x]=find(ffff[x]); return ffff[x];}
int ff1,ff2;
int main()
{
    n=read(),m=read(),a=read();
    for (int u,v,i=1; i<=n-1; i++)
        u=read(),v=read(),insert(u,v);
    dfs(1);
    for (int i=1; i<=n; i++) ffff[i]=i;
    for (int i=1; i<=m; i++)
        {
            int x=read();
            int fa=find(a),fx=find(x);
            if (fa==fx) continue;
            int lca=LCA(a,x);
            ans+=deep[a]-deep[lca]+deep[x]-deep[lca];
            int ta=a,tx=x,flca; flca=find(lca);
            while (find(ta)!=flca) {ff1=find(ta); ffff[ff1]=flca; ta=father[ff1][0];}
            while (find(tx)!=flca) {ff2=find(tx); ffff[ff2]=flca; tx=father[ff2][0];}
            a=x;
        }
    cout<<ans;
    return 0;
}

电脑炸出奇怪的错误,所以写的比较鬼畜- -

时间: 2024-10-19 00:04:13

【BZOJ-3910】火车 倍增LCA + 并查集的相关文章

【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集

[题意]给定n个点m条边的带边权无向连通图,对每条边求最大边权,满足其他边权不变的前提下图的任意最小生成树都经过它.n,m<=2*10^5,1<=wi<=10^9. [算法]最小生成树+倍增LCA+并查集 [题解]首先求出图的一个最小生成树,则所有边分成树边和非树边. 对于非树边(u,v),假设u和v在最小生成树上的路径的最大边权Max,那么一定满足w(u,v)<=Max /////////////////////////////////////// 原文地址:https://ww

【XSY2485】MST(最小生成树+倍增lca+并查集)

题面 Description 给定一个\(n\)个点\(m\)条边的连通图,保证没有自环和重边.对于每条边求出,在其他边权值不变的情况下,它能取的最大权值,使得这条边在连通图的所有最小生成树上.假如最大权值为无限大,则输出\(-1\). Input 第一行两个整数\(n\),\(m\),表示\(n\)个点\(m\)条边 接下来\(m\)行,每行\(3\)个整数\(x\),\(y\),\(z\),表示节点\(x\)和节点\(y\)之间有一条长\(z\)的边. Output 输出一行\(m\)个整数

BZOJ 3562: [SHOI2014]神奇化合物 并查集+dfs

点击打开链接 注意到20w条边,但是询问只有1w,所以有很多边是从头到尾不变的. 首先离线处理,将从未删除的边缩点,缩点后的图的点数不会超过2w,对于每一次add或者delete,直接dfs看是否能从a走到b,然后维护一个ans. 数据不强,不然这种复杂度起码要跑10s.. #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> using namespace st

BZOJ 3910 火车 LCA+并查集

题目大意 给出一棵树,起点,和要经过的点的序列,已经经过的点就不用去了,剩下的点按照顺序依次去,问要经过多少条边. 思路 链剖大概应该是可以,不过没试,用了听大爷说的一种神奇的方法. 因为树上经过的点肯定是一段一段的,就想到用并查集将一段合成一个点,每个点最多只能被合一次,这样的话就能保证时间复杂度.查询的时候像链剖一样一段一段往上跳就行了,还要顺便把路径上的所有点缩起来. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #incl

【BZOJ3910】火车 LCA+并查集

链接: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44780959"); } 题解: 首先找两点之间路径可以用倍增LCA. 然后标记哪个点走过可以用并查集,均摊下来最后是线性的. 代码: #include <cstdio> #include <cstring>

Hdu 5458 Stability (LCA + 并查集 + 树状数组 + 缩点)

题目链接: Hdu 5458 Stability 题目描述: 给出一个还有环和重边的图G,对图G有两种操作: 1 u v, 删除u与v之间的一天边 (保证这个边一定存在) 2 u v, 查询u到v的路径上有几条桥. 解题思路: 这个题目有很多次操作,包含查询和删边两类,首先想到的是连通分量加缩点.如果按照顺序来,删边时候求桥就是问题了.所以可以离线处理,然后一边记录答案一边加边缩点. 对于一个图,把连通分量缩成一个点后,这个图就成为了一棵树, 然后深度差就等于桥的数目.查询的时候对于(u, v)

HDU 5458 Stability(双连通分量+LCA+并查集+树状数组)(2015 ACM/ICPC Asia Regional Shenyang Online)

题目大意:给一个N个点M条边的无向图,有Q个询问:1.删掉a.b之间所存在的边:2.询问有多少条边,单独删掉之后a与b不再连通. 思路:脑洞大开. 对于询问,首先想到的就是a与b之间有多少桥(割边),然后想到双连通分量,然而删边是个坑爹的问题,于是我们离线倒着来,把删边变成加边. 双连通分量这种东西呢,其实缩点连起来之后,就是一棵树辣. 然后询问两个点的时候,设根到点x的距离为dep[x],a.b的最近公共祖先为lca(a, b),那么询问query(a, b) = dep[a] + dep[b

bzoj 2733 永无乡 - 并查集 - 线段树

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输

HDU 4297--One and One Story(LCA&amp;并查集)

One and One Story Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/Others)Total Submission(s): 1049 Accepted Submission(s): 459 Problem Description Have you ever played the romantic Flash game, "One and One Story"?1 In