2017 Shenyang onsite replay L - Tree

清泠,發表於2020-10-18

這裡主要補一下L題,感覺也是挺巧妙的,不過也不難

傳送門

題目大意:

給予一個無根樹,然後給予K種顏色,求將所有顏色的點都連線起來,這K種顏色最多有多少條公共的邊。

解題思路:

  • 首先看一個圖,我們直接拆分邊,如果這個邊的兩端都有>=k 個點,那麼這條邊就可以公共通過。

在這裡插入圖片描述

  • 假如K == 3, 那麼如果我切割中間的邊, 那麼左右的點的數目都是 >= 3 ,就符合條件,所以我們根據這個性質來繼續解題。
  • 因此我們只需要找一個點作為根即可。(找度為1的點,比較方便)
  • 然後我們記錄每個點有多少個子節點,然後我們去遍歷每個點,他的子節點 和 n - 子節點 ,判斷這兩個數值是否 >= k 即可,累加統計

程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <stack>
#include <cmath>
#include <deque>

using namespace std;

typedef long long ll;

const int N = 200010, M = 400010;
const int mod = 1e9 + 7;

pair <int,int> ans[N];

int h[N], e[M], ne[M], idx; 
int d[N];
int b[N];

int num = 1;

void add(int x, int y){
	e[idx] = y, ne[idx] = h[x], h[x] = idx ++;
}

void dfs(int u, int f){
	b[u] = 1;
	for (int i = h[u]; i != -1; i = ne[i]){
		int j = e[i];
		if (j != f){
			dfs(j,u);
		}
	}
	if (f != -1) b[f] += b[u];
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
    	idx = 0;
    	memset(d,0,sizeof d);
    	memset(h,-1,sizeof h);
    	int n, k;
    	scanf("%d%d",&n,&k);
    	for (int i = 1; i <= n - 1; i ++){
    		int x, y;
    		scanf("%d%d",&x,&y);
    		ans[i] = {x,y};
    		d[x] ++, d[y] ++;
    		add(x,y), add(y, x);
    	}
    	int res = 0;
    	int x;
    	for (int i = 1; i <= n; i++){
    		if (d[i] == 1){
    			x = i;
    		}
    	}
    	dfs(x, -1);

    	for (int i = 1; i <= n; i++){
    		if (b[i] >= k && n - b[i] >= k) res ++;
    	}
    	printf("%d\n",res);
    }
    return 0;
}

相關文章