日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2017 SEERC Divide and Conquer 树上差分

發布時間:2023/12/3 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2017 SEERC Divide and Conquer 树上差分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目

題目大意:給出兩顆樹的復合圖(即這張圖是由兩顆樹拼起來的),詢問最小割掉多少條邊,可以使得圖不聯通,并輸出方案數。

分析

我覺得這是一道很難的題目,因為比較難想,前提結論比較多。

首先我們需要得到一個結論:就是割掉的邊數不可能超過3。
證明:如果割掉的邊數超過3,那么意味著每個點的度數都要4≥4,這也就是說,圖中至少需要2n2n條邊,而圖是由兩個樹拼成的,邊數只有2n?22n?2條邊,顯然不可能。

既然割掉的邊數2e32≤e≤3,那么我們可以得到結論:割掉的邊既有屬于A樹的,也有屬于B樹的,如果割掉的邊數為2,那么說明A樹一條割邊,B樹一條割邊。
如果割掉的邊數為3,那么必有一棵樹只包含一條割邊。

因此,我們可以發現,如果我們以其中的一棵樹(舉例A樹,這棵樹只包含一條割邊)作為主樹的話,相當于將這顆樹切成兩邊,并且求交叉于這棵樹兩邊的B樹樹邊的數量。
如圖所示:

其中紅線表示切割的位置,其中割掉的邊數為 AA11條+BB22條 = 33 條。

最后一個問題,怎么求A樹的兩個部分中B樹橫跨了多少邊呢?
這就要用到樹上差分了,由于A樹的兩個部分中必有一個是A樹的子樹,這意味著我們可以使用dfs來遍歷,其次,B樹的邊如果鏈接的兩個點是屬于A樹一個部分的時候,那么對結果沒有影響。
因此,我們給B樹的邊(u,v)(u,v)做如下操作mk[u]++,mk[v]++,mk[lca(u,v)]?=2;mk[u]++,mk[v]++,mk[lca(u,v)]?=2;
這樣的話,我們只需要統計A的子樹有mkmk的和,就可以知道鏈接A樹的兩個部分有多少條B樹的邊了。

注意

當答案是2的時候,我們直接輸出方案數即可,而當答案是3的時候,我們需要交換A、B,并再次計算方案數,因為答案是3的情況方案數可能增加。

代碼

#include <iostream> #include <vector> #include <cstring> using namespace std; const int maxn = 1e5+7; vector<int> A[maxn],B[maxn],V[maxn]; int n,dep[maxn],pa[maxn][22],mk[maxn]; inline int getint(){int tmp;cin>>tmp;return tmp;} int mi = 1e9,ans = 0; void dfs(int u,int fa){pa[u][0] = fa;dep[u] = dep[fa] + 1;for(auto v : V[u]){if(v == fa) continue;dfs(v,u);} } #define pr(x) cout<<#x<<":"<<x<<endl int lca(int u,int v){if(dep[u] < dep[v]) swap(u,v);int d = dep[u] - dep[v];for(int i = 0;d;i++,d >>= 1) if(d & 1) u = pa[u][i];for(int i = 20;~i;i--) if(pa[u][i] != pa[v][i]) u = pa[u][i],v = pa[v][i];if(u != v) u = pa[u][0],v = pa[v][0];return u; }void dfs2(int u,int fa){for(int v : V[u]){if(v == fa) continue;dfs2(v,u);mk[u] += mk[v];}if(u == 1) return ;if(mk[u] + 1 < mi){mi = mk[u] + 1;ans = 1;}else if(mk[u] + 1 == mi) ++ans; }void solve(vector<int> A[maxn],vector<int> B[maxn]){for(int i = 1;i <= n;++i) V[i].clear();for(int u = 1;u <= n;++u) for(int v : A[u]){V[u].push_back(v);V[v].push_back(u);}memset(dep,0,sizeof(dep));memset(pa,0,sizeof(pa));memset(mk,0,sizeof(mk));dfs(1,0);for(int t = 1;t <= 20;++t){for(int i = 1;i <= n;++i){pa[i][t] = pa[pa[i][t-1]][t-1];}}for(int u = 1;u <= n;++u) for(int v : B[u]){mk[v] ++,mk[u] ++,mk[lca(u,v)] -= 2;}dfs2(1,0);} signed main() {ios::sync_with_stdio(false);n = getint();int u,v;for(int i = 0;i < n-1;++i) {u = getint();v = getint();A[u].push_back(v);}for(int i = 0;i < n-1;++i) {u = getint(),v = getint();B[u].push_back(v);}solve(A,B);if(mi == 2) return 0*printf("%d %d\n",mi,ans);solve(B,A);cout<<mi<<' '<<ans<<endl;return 0; }

總結

以上是生活随笔為你收集整理的2017 SEERC Divide and Conquer 树上差分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。