jzoj 5782. 【NOIP提高A组模拟2018.8.8】 城市猎人(lca+想法)

3 篇文章 0 订阅

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


分析:

最直观的想法就是连边,建出一棵树,用倍增维护lca。
但连边的时间复杂度为 O(n2) O ( n 2 ) ,只能拿到四十分。
观察题目的连边的条件,我们可以发现:
x=mi+1 x = m − i + 1 ,那么对于每个 x x x2x3xkx(kxn)会连成一片。
这样每个x会产生 nx n x 条边,所以总边数为 ni=1ninlogn ∑ i = 1 n n i ≈ n log ⁡ n 条。
那么我们可以暴力连边,枚举x,然后连边。
用并查集维护联通情况,最后建出一棵树,用倍增来处理询问
时间复杂度 O(nlogn) O ( n log ⁡ n )


code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e5+10;
int n,m,q,last[N],last1[N],fa[N],f[N][20][2],d[N],deep[N];
struct node{
    int a,b,c;
}a[2*N];
void add(int x,int y,int z) {
    a[++a[0].a].a=y;
    a[a[0].a].b=z;
    a[a[0].a].c=last[x];
    last[x]=a[0].a;
}
int getf(int x) {
    if (fa[x]==x || fa[x]==0) return x;
    fa[x]=getf(fa[x]);
    return fa[x];
}
void dfs(int x,int y) {
    int i,j,k;
    if (d[0]!=1) {
        f[x][0][0]=d[d[0]-1];
        f[x][0][1]=y;
    } 
    for (i=1;d[0]-(1<<i)>0;i++) {
        f[x][i][0]=d[d[0]-(1<<i)];
        f[x][i][1]=max(f[x][i-1][1],f[f[x][i-1][0]][i-1][1]);
    }
    for (i=last[x];i!=0;i=a[i].c) {
        if (a[i].a!=d[d[0]-1]) {
            d[++d[0]]=a[i].a;
            deep[a[i].a]=deep[x]+1;
            dfs(a[i].a,a[i].b);
            d[0]--;
        }
    }
}
int work(int x,int y) {
    int i;
    if (deep[x]<deep[y]) swap(x,y);
    int z=0;
    while (deep[x]!=deep[y]) {
        for (i=0;deep[f[x][i][0]]>=deep[y];i++);
        i--;
        z=max(z,f[x][i][1]);
        x=f[x][i][0];
    }
    while (x!=y) {
        for (i=0;f[x][i][0]!=f[y][i][0];i++);
        if (i==0) {
            z=max(z,max(f[x][i][1],f[y][i][1]));
            break;
        }
        i--;
        z=max(z,max(f[x][i][1],f[y][i][1]));
        x=f[x][i][0];
        y=f[y][i][0];
    }
    return z;
}
int main() {
    freopen("pictionary.in","r",stdin);
    freopen("pictionary.out","w",stdout);
    int i,j,k=0;
    scanf("%d%d%d",&n,&m,&q);
    for (i=1;i<=m;i++) {
        int t=m-i+1;
        for (j=1;(j+1)*t<=n;j++) {
            int x=j*t,y=(j+1)*t;
            if (getf(x)!=getf(y)) {
                k++;
                add(x,y,i);add(y,x,i);
                fa[getf(x)]=getf(y);
            }
        }
    }
    d[0]=d[1]=1;
    deep[1]=1;
    dfs(1,0);
    for (i=1;i<=q;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",work(x,y));
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值