作法:
這題我的作法是按照官方解的作法寫的,這裡大概提一些官方解裡比較沒有寫到的東西,還有我的實作方法。
首先是求出兩條路徑的共同部份(對了,我的記號是記兩條路徑分別是u_1~v_1和u_2~v_2),還有之後需要用到的重要的數字f_1,f_2,t_1,t_2,t_3,t_4,dis,其中f_1,f_2分別代表兩條路徑的兩倍長,dis則代表兩條路徑共同部份的長度,t_1,t_2,t_3,t_4則和題解中的定義一樣。這裡先假設在u_1,v_1,u_2,v_2中沒有任何一點是另一點的祖先,這種情形的算法可以直接套到一般情形上。至於為什麼我目前也不是很清楚,不過大概可以想成:如果X是Y的祖先,那麼可以把X替換成另一個虛擬節點Z,然後把X接到Z底下,並且在他們之間連一條長度為0的邊,這樣不會影響到我們要求的任何東西。有了這件事之後,可以發現給定的這四個點形成的圖形只會有以下兩種:
其中黑點代表給定的四個點,白點則是某兩點的LCA。雖然他們都長的像這兩種情形,不過他們對應到的黑色節點們會有很多種不同的排列方式。可以算出如果四個點的位置亂排的話,左邊有12種,右邊則有3種,並且不符合條件(也就是路徑沒有重合的部份)的排列左邊有4種,右邊有1種。因此總共有10種情形,我沒有想到比較好的作法,所以很白癡的都討論了一遍......具體的方法是,如果這四點是形成左邊這張圖的話,那麼一定存在一個點X,使得X和另外三點的LCA都一樣,也就是這時候我們就知道X就是左邊這張圖的左上角那個黑點。而去掉他之後,剩下的圖形也可以用類似的方法來判斷,也就是此時存在一個Y,使得他和另外兩點的LCA一樣(此時已排除剛剛找到的X),而他就會是圖中上面數來第二個黑點。至於右邊這種情況,注意到左邊兩黑點的LCA和右邊兩黑點的LCA是兄弟的關係,也就是互不為對方的祖先,所以就可以用這件事來判斷當前是否為這種情形。當確定此時是哪種情形的時候所有需要的值就都知道了,只是寫起來很麻煩而已......
再來則是轉換後的數論問題,第一部份是要能夠求出以下問題的答案:給定a,b,c,d\geq 0,求出在所有滿足ax+b=cy+d的整數(x,y)中,當ax+b為最小的非負值的時候其值是多少。移項變成ax-cy=d-b,因此可以直接套用擴展歐幾里得算法求出一組(x,y)的解,再把他調整成讓ax+b值最小的解。而在調整的時候如果是用慢慢加的會TLE,要算好要加幾倍再一次加上去才可以,看code應該比較好理解。
再來則是第二部份,也就是求g函數的值的部份,我覺得他寫的怪怪的所以自己弄了一個作法。這裡我的記號和他不太一樣,g(a,b,y,M)會回傳最小的非負整數x,使得a\leq (x\cdot y)\mod M\leq b,並且a,b滿足0\leq a\leq b<M。想像x從0開始慢慢往上加,那麼x\cdot y就會一直+y,當他加到\geq a的時候,如果他的值\leq b,那麼顯然此時的x就是最小的解了,直接回傳就可以了,也就是第一步是先確認\geq a的最小的y的倍數是否可行,如果可以的話就直接回傳。如果不行的話,那麼就代表a和b之間沒有任何y的倍數(包含a,b),這件事等一下會用到。那麼此時引入另一個變數r,把方程式改寫成a\leq x\cdot y-M\cdot r\leq b,也就是我們要求滿足上式的二元組(x,r)中,x的最小值是多少(當然r也要\geq 0)。而可以看出r越小x就越小,所以其實也可以先找出r的最小值再推出所求的值。再把這條式子反過來變成-b\leq r\cdot M-x\cdot y\leq -a,這時候因為a,b之間沒有y的倍數,所以可以把這條式子直接模y,把最左邊和最右邊都變成介於0到y-1的數字而不會反號(也就是變成((-b)\% y+y)\% y)。也就是現在變成了一個新的問題:求最小的r使得A\leq (r\cdot M)\mod y\leq B,其中A和B是剛才提到的值。注意到我們可以把M改成M\% y,也就是變成了A\leq (r\cdot (M\% y))\mod y\leq B,因此這樣就可以遞迴下去處理了,他的過程就和輾轉相除法類似,所以複雜度會是log級別的。另外還要注意到,如果此時M\% y = 0的話遞迴下去可能會有問題,不過不難發現此時一定無解,所以直接回傳就可以了。
另外在使用這個函式之前還會有些問題。回到官方解中我們要解決的那條式子,此時他形如yf_2+a\leq xf_1\leq yf_2+b,要求出最小的非負的x是多少。如果直接把a和b拿去模掉f_2然後丟g函式的話會有問題,因為有可能a和b之間有f_1的倍數,模下去之後就沒了之類的,因此我們先判斷x=0或y=0的時候這條式子有沒有解,如果有就直接回傳,沒有的話才用g函數來算。
對了,出題者在下面的留言說因為他作過了這題(也就是求g函數的值的裸題)所以才會的,可以在傳這題之前用來測測看g函數有沒有寫對。
code :
#include<bits/stdc++.h> #define LL long long #define INF (1LL<<60) using namespace std; const int maxn=200000+10 ; void gcd(LL a,LL b,LL &x,LL &y,LL d) { if(b==0){x=d/a ; y=0 ; return ;} gcd(b,a%b,y,x,d) ; y-=(a/b)*x ; } LL cal(LL a,LL b,LL c,LL d) /// a*x+b = c*y+d , return min a*x+b or INF { LL g=__gcd(a,c) ; if((b-d)%g) return INF ; LL x,y ; gcd(a,-c,x,y,d-b) ; LL dx=c/g , dy=a/g ; if(a*x+b>0) { LL t=(a*x+b-1)/(a*dx)+1 ; x-=t*dx , y-=t*dy ; } if(a*x+b<0) { LL t=(-a*x-b-1)/(a*dx)+1 ; x+=t*dx , y+=t*dy ; } return a*x+b ; } LL solve(LL a,LL b,LL y,LL M) /// find min x s.t. a <= (x*y)%M <= b { if(a==0) return 0 ; else if(y*((a-1)/y+1)<=b) return (a-1)/y+1 ; else if(M%y==0) return INF ; LL r=solve( ((-b)%y+y)%y , ((-a)%y+y)%y , M%y , y ) ; return r==INF ? INF : (a+r*M-1)/y+1 ; } vector<int> v[maxn] ; int tin[maxn],tout[maxn],tick ; int d[maxn],anc[18][maxn] ; void dfs(int x,int f,int dep) { anc[0][x]=f ; d[x]=dep ; tin[x]=tick++ ; for(int i=1;i<18;i++) anc[i][x]=anc[i-1][anc[i-1][x]] ; for(auto i : v[x]) if(i!=f) dfs(i,x,dep+1) ; tout[x]=tick++ ; } inline bool isfa(int x,int y) /// x is fa of y { return tin[x]<=tin[y] && tout[x]>=tout[y] ; } int LCA(int x,int y) { if(isfa(x,y)) return x ; if(isfa(y,x)) return y ; for(int i=17;i>=0;i--) if(!isfa(anc[i][x],y)) x=anc[i][x] ; return anc[0][x] ; } int u1,v1,u2,v2 ; int f1,f2,dis ; int t1,t2,t3,t4 ; bool getpath() { int p1=LCA(u1,v1) , p2=LCA(u2,v2) ; int p3=LCA(u1,u2) , p4=LCA(v1,v2) ; int p5=LCA(u1,v2) , p6=LCA(u2,v1) ; f1=2*(d[u1]+d[v1]-2*d[p1]) ; f2=2*(d[u2]+d[v2]-2*d[p2]) ; if(p1==p3 && p3==p5) /// u1 { if(p2==p4) /// v2 { dis=d[p6]-d[p2] ; t1=d[u1]+d[p2]-2*d[p3] ; t2=f1/2+d[v1]-d[p6] ; t3=f2/2+d[v2]-d[p2] ; t4=d[u2]-d[p6] ; } else if(p2==p6) /// u2 { dis=d[p4]-d[p2] ; t1=d[u1]+d[p2]-2*d[p3] ; t2=f1/2+d[v1]-d[p4] ; t3=d[u2]-d[p2] ; t4=f2/2+d[v2]-d[p4] ; } else return 0 ; } else if(p1==p4 && p4==p6) /// v1 { if(p2==p5) /// v2 { dis=d[p3]-d[p2] ; t1=d[u1]-d[p3] ; t2=f1/2+d[p2]+d[v1]-2*d[p1] ; t3=d[u2]-d[p3] ; t4=f2/2+d[v2]-d[p2] ; } else if(p2==p3) /// u2 { dis=d[p5]-d[p3] ; t1=d[u1]-d[p5] ; t2=f1/2+d[p3]+d[v1]-2*d[p1] ; t3=f2/2+d[v2]-d[p5] ; t4=d[u2]-d[p3] ; } else return 0 ; } else if(p2==p3 && p3==p6) /// u2 { if(p1==p4) /// v1 { dis=d[p5]-d[p1] ; t1=d[u1]-d[p5] ; t2=f1/2+d[v1]-d[p1] ; t3=f2/2+d[v2]-d[p5] ; t4=d[p1]+d[u2]-2*d[p3] ; } else if(p1==p5) /// u1 { dis=d[p4]-d[p1] ; t1=d[u1]-d[p1] ; t2=f1/2+d[v1]-d[p4] ; t3=d[p1]+d[u2]-2*d[p3] ; t4=f2/2+d[v2]-d[p4] ; } else return 0 ; } else if(p2==p4 && p4==p5) /// v2 { if(p1==p3) /// u1 { dis=d[p6]-d[p3] ; t1=d[u1]-d[p3] ; t2=f1/2+d[v1]-d[p6] ; t3=f2/2+d[v2]+d[p3]-2*d[p2] ; t4=d[u2]-d[p6] ; } else if(p1==p6) /// v1 { dis=d[p3]-d[p1] ; t1=d[u1]-d[p3] ; t2=f1/2+d[v1]-d[p1] ; t3=d[u2]-d[p3] ; t4=f2/2+d[p1]+d[v2]-2*d[p2] ; } else return 0 ; } else if(!isfa(p3,p4) && !isfa(p4,p3)) { dis=d[p3]+d[p4]-2*d[p1] ; t1=d[u1]-d[p3] ; t2=f1/2+d[v1]-d[p4] ; t3=d[u2]-d[p3] ; t4=f2/2+d[v2]-d[p4] ; } else if(!isfa(p5,p6) && !isfa(p6,p5)) { dis=d[p5]+d[p6]-2*d[p1] ; t1=d[u1]-d[p5] ; t2=f1/2+d[v1]-d[p6] ; t3=f2/2+d[v2]-d[p5] ; t4=d[u2]-d[p6] ; } else return 0 ; return 1 ; } void update(LL &ans,int type) { int T1=(type==1 ? t1 : t2) ; int T2=(type==1 ? t4 : t3) ; if((dis+T1+T2)%2) return ; int a=T2-T1-dis , b=T2-T1+dis ; /// y*f2 + a <= x*f1 <= y*f2 + b int x0=(a-1)/f1+1 , y0=(-b-1)/f2+1 ; if(a<=0 && b>=0){ans=min(ans,(LL)(dis+T1+T2)/2) ; return ;} if(a>=0 && x0*f1<=b) ans=min(ans,(LL)(dis+T1+T2+x0*f1)/2) ; else if(a<=0 && y0*f2+a<=0) ans=min(ans,(LL)(dis+T1+T2+y0*f2)/2) ; else { LL x=solve((a%f2+f2)%f2,(b%f2+f2)%f2,f1,f2) ; LL y=(x*f1-b-1)/f2+1 ; if(x!=INF) ans=min(ans,(dis+T1+T2+x*f1+y*f2)/2) ; } } main() { int n ; scanf("%d",&n) ; for(int i=1;i<n;i++) { int x,y ; scanf("%d%d",&x,&y) ; v[x].push_back(y) ; v[y].push_back(x) ; } dfs(1,1,0) ; int Q ; scanf("%d",&Q) ; while(Q--) { scanf("%d%d%d%d",&u1,&v1,&u2,&v2) ; if(!getpath()){printf("-1\n") ; continue ;} LL ans=INF ; ans=min(ans,cal(f1,t1,f2,t3)) ; ans=min(ans,cal(f1,t2,f2,t4)) ; update(ans,1) ; update(ans,2) ; printf("%I64d\n",ans==INF ? -1 : ans) ; } }
沒有留言:
張貼留言