[cf1361E]James and the Chase
稱一個點是"好點",當且僅當其到其餘所有點恰存在一條簡單路徑
結論1:$x$為好點當且僅當以$x$為根的dfs樹包含所有點且非樹邊均為返祖邊
若不包含所有點,那麼$x$到不被包含的點即不存在簡單路徑
若存在非樹邊不為返祖邊,則不論如何該邊$x$到該邊終點存在至少兩條簡單路徑
另一方面,考慮在這樣一棵dfs樹中的簡單路徑,將其用非樹邊劃分為若干段(段的內部只能用樹邊),每一段樹邊構成一條鏈,則顯然有以下性質:
1.鏈無公共點
2.第一條鏈鏈頂為起點,最後一條鏈鏈尾為終點
3.每一條鏈鏈尾存在到下一條鏈鏈頂的(返祖)邊
4.每一條鏈鏈頂是上一條鏈鏈頂的祖先(其實是1和3的推論)
由此,考慮從$x$到$y$的簡單路徑,最後一條鏈鏈頂是$x$的祖先(4的推論),而該鏈鏈尾為$y$,因此$x$到$y$的簡單路徑數必然經過$lca(x,y)$
特別的,若$x$是$y$的祖先,那麼為了不重複經過$x$,必然僅有一條鏈且恰為$x$到$y$
更特別的,當$x$是根時其是任意一點的祖先,因此到任意一點恰存在一條簡單路徑,即得證
根據此結論,即可$o(n)$判定一個好點
進一步的,不斷隨機一個點$x$並判斷其是否是好點,若隨機$T$次後仍找不到好點,那麼好點數嚴格小於20%的概率即有$1-\frac{1}{5^{T}}$,當$T=100$時可以看作1,也即不需要輸出
由此即可得到一個好點,將其記作$rt$,並以$rt$為根建立dfs樹(以下均指此樹)
稱一條非樹邊"通過"$x$當且僅當其以$x$子樹內(包括$x$)為起點且到達$x$的祖先
結論2:
$x=rt$的情況顯然,不妨假設$x\ne rt$
此時,如果不存在非樹邊通過$x$顯然$x$無法到達$x$的祖先,如果存在多條非樹邊通過$x$則顯然$x$到這兩條非樹邊終點中較深的點存在至少兩條簡單路徑,因此$x$均不為好點
而若該非樹邊的終點不為好點,對其分類討論:
1.若其到某點不存在簡單路徑,注意到其可以到達$x$,那麼$x$到該點一定也不存在簡單路徑
2.若其到某點存在至少兩條簡單路徑,該點顯然不在其子樹中(結合結論1的證明),那麼其一定不會再經過$x$(到達$x$子樹內後要離開必須重複經過自己),也即$x$到其的這一段不會使得簡單路徑重複經過某點,那麼$x$到該點也存在至少兩條簡單路徑
另一方面,若該非樹邊的終點是好點,則對其餘點分類討論:
1.$x$到$x$子樹內的點,同樣根據結論1的證明恰存在一條簡單路徑
2.$x$到$x$子樹外的點,必然要經過該好點且之後不會在經過$x$,在其簡單路徑的基礎上補一段即可
由此,簡單遞迴即可求出所有好點
時間複雜度為$o(Tn)$,可以通過
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define T 100 5 vector<int>ans,v[N]; 6 int t,n,m,rt,flag,x,y,dfn[N],cnt[N],pos[N],vis[N]; 7 void dfs(int k){ 8 dfn[k]=++dfn[0],vis[k]=1; 9 for(int i=0;i<v[k].size();i++) 10 if (!dfn[v[k][i]]){ 11 dfs(v[k][i]); 12 if ((!pos[k])||(dfn[pos[v[k][i]]]<dfn[pos[k]]))pos[k]=pos[v[k][i]]; 13 cnt[k]+=cnt[v[k][i]]; 14 } 15 else{ 16 if (!vis[v[k][i]])flag=1; 17 else{ 18 if ((!pos[k])||(dfn[v[k][i]]<dfn[pos[k]]))pos[k]=v[k][i]; 19 cnt[k]++,cnt[v[k][i]]--; 20 } 21 } 22 vis[k]=0; 23 } 24 void check(int k){ 25 vis[k]=0; 26 if ((k==rt)||(cnt[k]==1)&&(vis[pos[k]]))vis[k]=1; 27 for(int i=0;i<v[k].size();i++) 28 if (dfn[k]<dfn[v[k][i]])check(v[k][i]); 29 } 30 int main(){ 31 srand(time(0)); 32 scanf("%d",&t); 33 while (t--){ 34 scanf("%d%d",&n,&m); 35 ans.clear(); 36 for(int i=1;i<=n;i++)v[i].clear(); 37 for(int i=1;i<=m;i++){ 38 scanf("%d%d",&x,&y); 39 v[x].push_back(y); 40 } 41 for(int k=0;k<T;k++){ 42 rt=flag=dfn[0]=0; 43 for(int i=0;i<20;i++)rt=(rt<<1)+rand()%2; 44 rt=(rt+n-1)%n+1; 45 for(int i=1;i<=n;i++)dfn[i]=cnt[i]=pos[i]=vis[i]=0; 46 dfs(rt); 47 if ((dfn[0]!=n)||(flag))continue; 48 for(int i=1;i<=n;i++)vis[i]=0; 49 check(rt); 50 for(int i=1;i<=n;i++) 51 if (vis[i])ans.push_back(i); 52 break; 53 } 54 if (5*ans.size()<n)printf("-1\n"); 55 else{ 56 printf("%d",ans[0]); 57 for(int i=1;i<ans.size();i++)printf(" %d",ans[i]); 58 printf("\n"); 59 } 60 } 61 return 0; 62 }View Code