[数论][LCA][并查集]JZOJ 5782 城市猎人
Posted mastervan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数论][LCA][并查集]JZOJ 5782 城市猎人相关的知识,希望对你有一定的参考价值。
分析
显然对于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 ",Ask(a,b)); } fclose(stdin);fclose(stdout); }
以上是关于[数论][LCA][并查集]JZOJ 5782 城市猎人的主要内容,如果未能解决你的问题,请参考以下文章