[NOIP模拟]城市猎人

题目

题目背景

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

输入描述

第一行输入三个正整数 \(n,m,q\),其中 \(q\) 表示询问个数。

接下来 \(q\) 行,每行两个正整数 \(x,y\),表示询问城市 \(x\) 和城市 \(y\) 最早什么时候连通。

输出描述

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

题解

明白一点,两点间联通时间取决于它们之间最晚建好的一条边,那么我们可以有一个思路雏形:将需要建边的点建边,对于询问两点就找一下最大边最小的路径.

但是,如果我们暴力建边,我们需要建的边数可视地十分巨大,那么我们能否有更好的方法建边?

注意到每次建边的点肯定是 \(m-i+1\) 的倍数,那么我们建边时就可以采用超级点的做法,将 \(m-i+1\) 与它的所有倍数建边,这样在取最大边权的意义下,相当于它们之间都建了边权为 \(i\) 的边.

而处理询问有更好的做法,我们可以将原来的图放到带权并查集上,对于并查集按秩合并,这样高度不超过 \(\log n\),对于询问的两点,我们直接用类似暴力爬山处理即可.

建边复杂度为 \(\mathcal O(m\log^2 n)\),询问复杂度为 \(\mathcal O(q\log n)\).

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	#define rep(i,l,r) for(int i=l,i##_end_=r;i<=i##_end_;++i)
	#define fep(i,l,r) for(int i=l,i##_end_=r;i>=i##_end_;--i)
	#define fi first
	#define se second
	#define Endl putchar('\n')
    #define writc(x,c) fwrit(x),putchar(c)
	typedef long long ll;
	typedef pair<int,int> pii;
	template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
	template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
	template<class T>inline T fab(const T x){return x<0?-x:x;}
	template<class T>inline void getMax(T& x,const T y){x=Max(x,y);}
	template<class T>inline void getMin(T& x,const T y){x=Min(x,y);}
	template<class T>T gcd(const T x,const T y){return y?gcd(y,x%y):x;}
	template<class T>inline T readin(T x){
		x=0;int f=0;char c;
		while((c=getchar())<'0' || '9'<c)if(c=='-')f=1;
		for(x=(c^48);'0'<=(c=getchar()) && c<='9';x=(x<<1)+(x<<3)+(c^48));
		return f?-x:x;
	}
    template<class T>void fwrit(const T x){
        if(x<0)return putchar('-'),fwrit(-x);
        if(x>9)fwrit(x/10);putchar(x%10^48);
    }
}
using namespace IO;

const int maxn=1e5;

int fa[maxn+5],w[maxn+5],d[maxn+5],sz[maxn+5];

inline int find(const int u){
    if(fa[u]==u)return d[u]=1,u;
    int ret=find(fa[u]);
    d[u]=d[fa[u]]+1;
    return ret;
}

inline void add(const int u,const int v,const int c){
    // printf("add :> u == %d, v == %d, c == %d\n",u,v,c);
    int x=find(u),y=find(v);
    if(x!=y){
        if(sz[x]<sz[y])swap(x,y);
        getMax(sz[x],sz[y]+1);
        fa[y]=x,w[y]=c;
    }
}

int n,m,q;

signed main(){
    // freopen("pictionary.in","r",stdin);
    // freopen("pictionary.out","w",stdout);
    n=readin(1),m=readin(1),q=readin(1);
    rep(i,1,n)fa[i]=i,sz[i]=1,d[i]=1;
    rep(i,1,m){int now=m-i+1;
        for(int j=1;now*j+now<=n;++j)
            add(now,now*j+now,i);
    }
    while(q--){
        int u=readin(1),v=readin(1);
        int ans=0;
        find(u),find(v);// 要先将 d 更新
        if(d[u]<d[v])swap(u,v);
        while(d[u]>d[v])getMax(ans,w[u]),u=fa[u];
        while(u!=v){
            getMax(ans,Max(w[u],w[v]));
            u=fa[u],v=fa[v];
        }writc(ans,'\n');
    }
	return 0;
}
posted @ 2020-11-28 16:44  Arextre  阅读(73)  评论(0编辑  收藏  举报