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) = m - i + 1 的点对连边,由于要求的是最早联通时间,之前已经联通则不管后面的边,我们用并查集维护一下连通性即可
这样最后会变成一棵树,暴力枚举 m - i + 1 的倍数建边即可,树边上记录联通时间
每个询问的答案就是路径 x--lca(x, y)--y 上的边权的最大值
发现根本不用建树,在并查集上按秩合并维护树的形态即可,这样最大深度是 log 级别的,lca 也就不用倍增什么的了 = =
暴力乱搞就行了
由于是用并查及维护的信息,需要及时 findfa 更新必要的信息
代码:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cctype> #include<cstdio> using namespace std; const int MAXN = 100005; int n, m, q, blk; int fa[MAXN], siz[MAXN], d[MAXN], dep[MAXN]; int findfa(int x) { if(fa[x] != x) dep[x] = dep[fa[x]] + 1; else return x; return findfa(fa[x]); } inline void link(int x, int y, int v) { int fx = findfa(x), fy = findfa(y); if(fx == fy) return; --blk; if(siz[fx] <= siz[fy]) { d[fx] = v; fa[fx] = fy; siz[fy] += siz[fx]; findfa(fy); } else { d[fy] = v; fa[fy] = fx; siz[fx] += siz[fy]; findfa(fx); } return; } inline int query(int xx, int yy) { findfa(xx); findfa(yy); if(dep[yy] > dep[xx]) swap(xx, yy); int res = 0; while(dep[xx] != dep[yy]) { res = max(res, d[xx]); xx = fa[xx]; } while(xx != yy) { res = max(res, max(d[xx], d[yy])); xx = fa[xx]; yy = fa[yy]; } return res; } int main() { freopen("pictionary.in", "r", stdin); freopen("pictionary.out", "w", stdout); scanf("%d%d%d", &n, &m, &q); blk = n; for(int i = 1; i <= n; ++i) {fa[i] = i; siz[i] = 1;} for(int i = 1; i <= m; ++i) { int cur = m - i + 1, mul = 2, last = m - i + 1; while(cur * mul <= n) { link(last, cur * mul, i); last = cur * mul; ++mul; if(blk == 1) break; } if(blk == 1) break; } register int xx, yy; while(q--) { scanf("%d%d", &xx, &yy); printf("%d ", query(xx, yy)); } return 0; }