HDU5927
题意:给出一个根节点为1的树(n <= 100000),树中的结点分为重要结点与不重要结点。
有q个询问,每次询问给出一个不重要结点的集合(m <= 100000), 统计一类点(它是重要结点 或者 它是两个重要结点的最近公共祖先) 的总数。
题解:维护一个set集合表示以该结点为根的子树不含重要结点,set集合初始即为m个点。按 后序遍历 将集合中的点排序后(ra表示结点后序遍历中的排名),对每个点,查找子结点点,若至少有两个子结点不在set里,则该点是可行点,同时将其从集合中删除。
复杂度分析:每次询问的复杂度为O(mlogm)
time: 1794MS
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5+10; 4 vector<int> ve[N]; 5 int ra[N], tot; 6 void dfs(int f, int x){ 7 for(int i = 0; i < ve[x].size(); i++){ 8 int y = ve[x][i]; 9 if(y == f) continue ; 10 dfs(x, y); 11 } 12 ra[x] = tot++; 13 } 14 bool cmp(int a, int b){ 15 return ra[a] < ra[b]; 16 } 17 18 19 int temp[N]; 20 int tag[N]; 21 int main(){ 22 int t, ca = 1; scanf("%d", &t); 23 while(t--){ 24 int n, q, u, v; scanf("%d%d", &n, &q); 25 for(int i = 1; i < n; i++){ 26 scanf("%d%d", &u, &v); 27 ve[u].push_back(v); 28 ve[v].push_back(u); 29 } 30 tot = 0; 31 dfs(0, 1); 32 printf("Case #%d:\n", ca++); 33 while(q--){ 34 int m, x; scanf("%d", &m); 35 for(int i = 0; i < m; i++) 36 scanf("%d", temp+i); 37 sort(temp, temp+m, cmp); 38 set<int> se; 39 for(int i = 0; i < m; i++) 40 se.insert(temp[i]); 41 int ans = 0; 42 for(int i = 0; i < m; i++){ 43 int x = temp[i], sum = 0; 44 for(int j = 0; j < ve[x].size(); j++){ 45 int y = ve[x][j]; 46 if(ra[y] > ra[x]) continue; 47 if(se.find(y) == se.end()){ 48 sum++; 49 if(sum > 1){ 50 ans++; 51 break; 52 } 53 se.erase(x); 54 } 55 } 56 } 57 printf("%d\n", n-m+ans); 58 } 59 for(int i = 1; i <= n; i++) 60 ve[i].clear(); 61 } 62 return 0; 63 }
时间: 2024-10-18 06:18:00