[NOIP模擬]城市獵人
阿新 • • 發佈:2020-11-28
題目
題目背景
有 \(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; }