1. 程式人生 > 其它 >[cf1361E]James and the Chase

[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$為好點當且僅當$x=rt$或恰存在一條非樹邊通過$x$且該非樹邊的終點為好點

$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