[数论][LCA][并查集]JZOJ 5782 城市猎人

Description

有n个城市,标号为1到n,修建道路花费m天,第i天时,若gcd(a,b)=m-i+1,则标号为a的城市和标号为b的城市会建好一条直接相连的道路,有多次询问,每次询问某两座城市最早什么时候能连通。

Input

第一行输入三个正整数n,m,q,其中q表示询问个数。
接下来q行,每行两个正整数x,y,表示询问城市x和城市y最早什么时候连通。

Output

输出q行,每行一个正整数,表示最早连通的天数

Sample Input

Input 1
8 3 3
2 5
3 6
4 8
Input 2
25 6 1
20 9
Input 3
9999 2222 2
1025 2405
3154 8949
 

Sample Output

Output 1
3
1
2
Output 2
4
Output 3
1980
2160
 

Data Constraint

对于40%的数据,n≤ 1000,q<=100000
对于100%的数据,1 ≤ n,q≤ 100000,1<=m<=q

分析

显然对于gcd(a,b)=k,a与b都是k的倍数且a/k+1=b/k

那么我们就知道,第i天会有这样一个数列变得连通:m-i+1,2*(m-i+1),3*(m-i+1),...

那么问题就变成了求问两个连通块之间最早的连通时间

我们想到了并查集,但是不能路径压缩,这样会破坏数据详细性

只能按秩合并咯

然后对于每个点给一个ti表示i这个点与父亲相连通的最早时间

那么我们预处理好并查集,求x,y到它们LCA(不包括LCA,注意!)的max{ti 即可

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=1+1e5;
int n,m,q;
int f[N],r[N],t[N],d[N];

int Get(int x) {
    if (x==f[x]) return x;
    int root=Get(f[x]);
    d[x]=d[f[x]]+1;
    return root;
}

int Ask(int x,int y) {
    int ans=0;
    if (d[x]<d[y]) swap(x,y);
    while (d[x]>d[y]) ans=max(ans,t[x]),x=f[x];
    while (x!=y) {
        ans=max(ans,max(t[x],t[y]));
        x=f[x];y=f[y];
    }
    return ans;
}

int main() {
    freopen("pictionary.in","r",stdin);
    freopen("pictionary.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    for (int i=1;i<=n;i++) f[i]=i,r[i]=1;
    for (int i=1;i<=m;i++) {
        for (int j=2;j<=n/(m-i+1);j++) {
            int u=Get((m-i+1)*(j-1)),v=Get((m-i+1)*j);
            if (u==v) continue;
            if (r[u]<=r[v]) {
                f[u]=v;t[u]=i;
                r[v]=max(r[u]+1,r[v]);
            }
            else {
                f[v]=u;t[v]=i;
                r[u]=max(r[v]+1,r[u]);
            }
        }
    }
    for (int i=1;i<=n;i++) Get(i);
    for (int i=0;i<q;i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        Get(a);Get(b);
        printf("%d\n",Ask(a,b));
    }
    fclose(stdin);fclose(stdout);
}

原文地址:https://www.cnblogs.com/mastervan/p/9445484.html

时间: 2024-10-07 20:04:14

[数论][LCA][并查集]JZOJ 5782 城市猎人的相关文章

【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

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 3910 火车 LCA+并查集

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

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

3910: 火车 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 262  Solved: 90[Submit][Status][Discuss] Description A 国有n 个城市,城市之间有一些双向道路相连,并且城市两两之间有唯一路径.现在有火车在城市 a,需要经过m 个城市.火车按照以下规则行驶:每次行驶到还没有经过的城市中在 m 个城市中最靠前的.现在小 A 想知道火车经过这m 个城市后所经过的道路数量. Input 第一行三个整数

【BZOJ3910】火车 LCA+并查集

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

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

Network POJ - 3694(lca并查集+连通图求桥)

就是求出原先图中的桥的数量,在每一次询问时加入一条新边,求加入当前边后图中剩余的桥的数量 求出原先图中的桥的数量,然后减去新加入边的两端点之间的桥的数量,就是剩余桥的数量.. 用并查集把属于同一集合的放到一起(即两个点之间没有桥的) #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algo

CodeForces 593D Happy Tree Party [LCA+并查集]

题意:给一棵树,每条边有一个权值,给两种操作,第一种是询问y向下整除从a到b的最短路径中每条边的权值后y的值,第二种是改变某条边的权值. 思路:y的最大值为1e18,最多除大于等于2的数不超过60次即可将y变为0,先dfs以任意一点为根建树,记录每个点的深度和它的父结点并将边权转化为点权, 再搞个并查集,将权值为1的点压缩,即使pre[u]=g[u];(u变成u的爸爸). 1 #include<bits/stdc++.h> 2 #define fi first 3 #define se sec