codeforces 862B Mahmoud and Ehab and the bipartiteness (DFS或带权并查集)
Mahmoud and Ehab continue their adventures! As everybody in the evil land knows, Dr. Evil likes bipartite graphs, especially trees.
A tree is a connected acyclic graph. A bipartite graph is a graph, whose vertices can be partitioned into 2 sets in such a way, that for each edge (u, v) that belongs to the graph, u and v belong to different sets. You can find more formal definitions of a tree and a bipartite graph in the notes section below.
Dr. Evil gave Mahmoud and Ehab a tree consisting of n nodes and asked them to add edges to it in such a way, that the graph is still bipartite. Besides, after adding these edges the graph should be simple (doesn't contain loops or multiple edges). What is the maximum number of edges they can add?
A loop is an edge, which connects a node with itself. Graph doesn't contain multiple edges when for each pair of nodes there is no more than one edge between them. A cycle and a loop aren't the same .
The first line of input contains an integer n — the number of nodes in the tree (1 ≤ n ≤ 105).
The next n - 1 lines contain integers u and v (1 ≤ u, v ≤ n, u ≠ v) — the description of the edges of the tree.
It's guaranteed that the given graph is a tree.
Output one integer — the maximum number of edges that Mahmoud and Ehab can add to the tree while fulfilling the conditions.
3
1 2
1 3
0
5
1 2
2 3
3 4
4 5
2
Tree definition: https://en.wikipedia.org/wiki/Tree_(graph_theory)
Bipartite graph definition: https://en.wikipedia.org/wiki/Bipartite_graph
In the first test case the only edge that can be added in such a way, that graph won't contain loops or multiple edges is (2, 3), but adding this edge will make the graph non-bipartite so the answer is 0.
In the second test case Mahmoud and Ehab can add edges (1, 4) and (2, 5).
分析:要把所有的点分成两个集合,那么最多的情况就是一个集合中所有的点都连接另一个集合中所有的点..
需要注意的是 此时它是一个树结构也就是说所有的点都直接或间接的存在着关系
那么当我们从一个点开始DFS的时候,奇数次的时候扫的点属于一个集合,偶数次的点属于另一个集合,这样的话,就能找出最多的边数,减去现在已有的边数就能得到答案
DFS:
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <vector> using namespace std; typedef long long ll; vector<int>V[100100]; int vis[100100]; ll num0; ll num1; void dfs(int x,int step) { for(int i=0;i<V[x].size();i++) { if(!vis[V[x][i]]) { vis[V[x][i]]=1; if(step%2==1)num0++; else num1++; dfs(V[x][i],step+1); } } } int main() { int n,a,b; scanf("%d",&n); for(int k=0;k<n-1;k++){ num1=0; scanf("%d%d",&a,&b); V[a].push_back(b); V[b].push_back(a); } num0=1; vis[1]=1; dfs(1,0); cout<<num0*num1-(n-1)<<endl; return 0; }
还可以从带权并查集来考虑,用0和1的权值将所有的点分成两个集合,需要注意的是每个点在最后Find操作进行,累加之后才是最后的权值
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <queue> #include <vector> using namespace std; typedef long long ll; int F[100010]; int val[100010]; int Find(int x) { if(F[x]==-1)return x; int tmp=Find(F[x]); val[x]+=val[F[x]]; val[x]%=2; return F[x]=tmp; } int main() { int n,a,b; ll num0,num1; scanf("%d",&n); memset(F,-1,sizeof(F)); memset(val,0,sizeof(val)); num0=0; num1=0; for(int k=0;k<n-1;k++) { scanf("%d%d",&a,&b); int t1=Find(a); int t2=Find(b); if(t1!=t2) { F[t2]=t1; val[t2]=(val[a]-val[b]+1+2)%2; } // cout<<val[a]<<" "<<val[b]<<endl; } for(int k=1;k<=n;k++) { int t1=Find(k);//这样才能得到该点最后的权值 if(val[k]==0) num0++; else num1++; } // cout<<num0<<" "<<num1<<endl; cout<<num0*num1-(n-1)<<endl; return 0; }