2017 SEERC Divide and Conquer 树上差分
題目
題目大意:給出兩顆樹的復合圖(即這張圖是由兩顆樹拼起來的),詢問最小割掉多少條邊,可以使得圖不聯通,并輸出方案數。
分析
我覺得這是一道很難的題目,因為比較難想,前提結論比較多。
首先我們需要得到一個結論:就是割掉的邊數不可能超過3。
證明:如果割掉的邊數超過3,那么意味著每個點的度數都要≥4≥4,這也就是說,圖中至少需要2n2n條邊,而圖是由兩個樹拼成的,邊數只有2n?22n?2條邊,顯然不可能。
既然割掉的邊數2≤e≤32≤e≤3,那么我們可以得到結論:割掉的邊既有屬于A樹的,也有屬于B樹的,如果割掉的邊數為2,那么說明A樹一條割邊,B樹一條割邊。
如果割掉的邊數為3,那么必有一棵樹只包含一條割邊。
因此,我們可以發現,如果我們以其中的一棵樹(舉例A樹,這棵樹只包含一條割邊)作為主樹的話,相當于將這顆樹切成兩邊,并且求交叉于這棵樹兩邊的B樹樹邊的數量。
如圖所示:
其中紅線表示切割的位置,其中割掉的邊數為 AA樹11條+BB樹22條 = 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 树上差分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 叠元宝教程 叠元宝教程简述
- 下一篇: 林小苏李奥萌萌是什么电视 独生子演员表