考察知识点:树上差分
给定一棵由 n 个节点组成的树以及 m 个不重复的无序数对(a1,b1)(a2,b2) (a3,b3)......(am,bm),其中ai互不相同,bi互不相同。ai,bi(1 <= i,j <= m)。小明想知道是否能够选择一条树上的边砍断,使得对于每个(ai,bi)满足ai,bi不连通,如果可以则输出应该断掉的边的编号(编号按输入顺序从一开始),否则输出 -1。
输入n + m行,第一行为两个正整数n,m。后面n - 1行,每行两个整数xi,yi表示第i条边的两个端点。后面m行,每行两个正整数ai,bi。
一行一个整数,表示答案,如果有多个答案,输出编号最大的一个。
1、按顺序输入每条边(无向边),使用邻接表储存图。
2、使用dfs方法确定每个点所处的层次,0处于第0层,以1为根节点,处于第1层。
3、输入每个数对a,b,d[a] ++,d[b] ++,求出点a与点b最近的公共祖先节点p,d[p] -= 2;
4、使用深度遍历每个点,求出每条边当前的权值,如果等于m则表示这条边可以砍掉,如果边的编号大于ans,则使用ans保存。
5、输出答案。
- #include
- using namespace std;
- const int N = 101000, M = 2 * N;
- int n,m;
- int h[N],e[M],ne[M],idx;
- int depth[N],fa[N][25];
- int d[N];
- int q[N];
- int ans;
- void add(int a,int b)
- {
- e[idx] = b,ne[idx] = h[a], h[a] = idx ++;
- }
-
- void bfs()
- {
- memset(depth,0x3f,sizeof depth);
- depth[0] = 0;
- depth[1] = 1;
- queue<int> q;
- q.push(1);
- while(!q.empty())
- {
- int t = q.front();
- q.pop();
- for(int i = h[t]; ~i; i = ne[i])
- {
- int j = e[i];
- if(depth[j] > depth[t] + 1)
- {
- depth[j] = depth[t] + 1;
- q.push(j);
- fa[j][0] = t;
- for(int k = 1; k <= 16; k ++)
- fa[j][k] = fa[fa[j][k - 1]][k - 1];
- }
- }
- }
- }
-
- int lca(int a,int b)
- {
- if(depth[a] < depth[b]) swap(a,b);
- for(int k = 16; k >= 0; k --)
- if(depth[fa[a][k]] >= depth[b])
- a = fa[a][k];
- if(a == b) return a;
- for(int k = 16; k >= 0; k --)
- if(fa[a][k] != fa[b][k])
- {
- a = fa[a][k];
- b = fa[b][k];
- }
- return fa[a][0];
- }
-
- int dfs(int u,int father,int id)
- {
- int res = d[u];
- for(int i = h[u]; ~i; i = ne[i])
- {
- int j = e[i];
- if(j == father) continue;
- int s = dfs(j,u,i);
- res += s;
- }
- if(res == m) ans = max(ans,id / 2 + 1);
- return res;
- }
-
- int main()
- {
- ans = -1;
- cin >> n >> m;
- memset(h,-1,sizeof h);
- for(int i = 0; i < n - 1; i ++)
- {
- int a,b;
- cin >> a >> b;
- add(a,b),add(b,a);
- }
- bfs();
- for(int i = 0; i < m; i ++)
- {
- int a,b;
- cin >> a >> b;
- int p = lca(a,b);
- d[a] ++,d[b] ++,d[p] -= 2;
- }
- dfs(1,-1,0);
- cout << ans << endl;
- return 0;
- }