$\sum k \leq 100000$虚树套路题
设$f_{i,0/1}$表示处理完$i$以及其所在子树的问题,且处理完后$i$所在子树内是否存在$1$个关键点满足它到$i$的路径上不存在任何点被封的最小代价。
转移考虑$i$是否是关键点以及是否封$i$号点。
注意判断相邻两个点同时是关键点的情况。同时需要注意:如果虚树上两个点不相邻,可以封这两个点之间的点。
1 #include<bits/stdc++.h>
2 //This code is written by Itst
3 using namespace std;
4
5 inline int read(){
6 int a = 0;
7 bool f = 0;
8 char c = getchar();
9 while(c != EOF && !isdigit(c)){
10 if(c == '-')
11 f = 1;
12 c = getchar();
13 }
14 while(c != EOF && isdigit(c)){
15 a = (a << 3) + (a << 1) + (c ^ '0');
16 c = getchar();
17 }
18 return f ? -a : a;
19 }
20
21 const int MAXN = 100010;
22 struct Edge{
23 int end , upEd;
24 }Ed[MAXN << 1] , newEd[MAXN];
25 int head[MAXN] , jump[MAXN][20] , newHead[MAXN] , s[MAXN] , dfn[MAXN] , num[MAXN] , dp[MAXN][2] , dep[MAXN];
26 int N , headS , cntEd , cntNewEd , ts , cnt;
27 bool imp[MAXN];
28
29 inline void addEd(Edge* Ed , int* head , int& cntEd , int a , int b){
30 Ed[++cntEd].end = b;
31 Ed[cntEd].upEd = head[a];
32 head[a] = cntEd;
33 }
34
35 void init(int now , int fa){
36 dfn[now] = ++ts;
37 dep[now] = dep[fa] + 1;
38 jump[now][0] = fa;
39 for(int i = 1 ; jump[now][i - 1] ; ++i)
40 jump[now][i] = jump[jump[now][i - 1]][i - 1];
41 for(int i = head[now] ; i ; i = Ed[i].upEd)
42 if(Ed[i].end != fa)
43 init(Ed[i].end , now);
44 }
45
46 inline int jumpToLCA(int x , int y){
47 if(dep[x] < dep[y])
48 swap(x , y);
49 for(int i = 19 ; i >= 0 ; --i)
50 if(dep[x] - (1 << i) >= dep[y])
51 x = jump[x][i];
52 if(x == y)
53 return x;
54 for(int i = 19 ; i >= 0 ; --i)
55 if(jump[x][i] != jump[y][i]){
56 x = jump[x][i];
57 y = jump[y][i];
58 }
59 return jump[x][0];
60 }
61
62 inline void create(){
63 imp[1] = 0;
64 dp[1][0] = dp[1][1] = 0;
65 for(int i = 1 ; i <= cnt ; ++i){
66 imp[num[i]] = 1;
67 dp[num[i]][1] = 0;
68 dp[num[i]][0] = N + 1;
69 }
70 for(int i = 1 ; i <= cnt ; ++i)
71 if(!headS)
72 s[++headS] = num[i];
73 else{
74 int t = jumpToLCA(s[headS] , num[i]);
75 if(t != s[headS]){
76 while(dep[s[headS - 1]] > dep[t]){
77 addEd(newEd , newHead , cntNewEd , s[headS - 1] , s[headS]);
78 --headS;
79 }
80 addEd(newEd , newHead , cntNewEd , t , s[headS]);
81 if(s[--headS] != t)
82 s[++headS] = t;
83 }
84 s[++headS] = num[i];
85 }
86 while(headS - 1){
87 addEd(newEd , newHead , cntNewEd , s[headS - 1] , s[headS]);
88 --headS;
89 }
90 if(s[headS] != 1)
91 addEd(newEd , newHead , cntNewEd , 1 , s[headS]);
92 --headS;
93 }
94
95 void dfs(int now){
96 int sum = 0;
97 for(int i = newHead[now] ; i ; i = newEd[i].upEd){
98 int t = newEd[i].end;
99 dfs(t);
100 if(imp[now])
101 if(jump[t][0] == now)
102 dp[now][1] += dp[t][0];
103 else
104 dp[now][1] += min(dp[t][0] , dp[t][1] + 1);
105 else{
106 if(jump[t][0] == now)
107 dp[now][1] = min(sum + dp[t][1] , dp[now][1] + dp[t][0]);
108 else
109 dp[now][1] = min(sum + dp[t][1] , dp[now][1] + min(dp[t][0] , dp[t][1] + 1));
110 if(jump[t][0] == now)
111 sum += dp[t][0];
112 else
113 sum += min(dp[t][0] , dp[t][1] + 1);
114 }
115 if(dp[now][1] > N)
116 dp[now][1] = N + 1;
117 if(sum > N + 1)
118 sum = N + 1;
119 }
120 sum = 0;
121 for(int i = newHead[now] ; i ; i = newEd[i].upEd){
122 int t = newEd[i].end;
123 if(!imp[now]){
124 sum += min(dp[t][0] , dp[t][1]);
125 dp[now][0] += dp[t][0];
126 }
127 dp[t][0] = dp[t][1] = imp[t] = 0;
128 }
129 if(!imp[now])
130 dp[now][0] = min(dp[now][0] , sum + 1);
131 newHead[now] = 0;
132 }
133
134 bool cmp(int a , int b){
135 return dfn[a] < dfn[b];
136 }
137
138 int main(){
139 #ifndef ONLINE_JUDGE
140 freopen("613D.in" , "r" , stdin);
141 //freopen("613D.out" , "w" , stdout);
142 #endif
143 N = read();
144 for(int i = 1 ; i < N ; ++i){
145 int a = read() , b = read();
146 addEd(Ed , head , cntEd , a , b);
147 addEd(Ed , head , cntEd , b , a);
148 }
149 init(1 , 0);
150 for(int M = read() ; M ; --M){
151 cnt = read();
152 for(int i = 1 ; i <= cnt ; ++i)
153 num[i] = read();
154 sort(num + 1 , num + cnt + 1 , cmp);
155 create();
156 dfs(1);
157 int t = min(dp[1][0] , dp[1][1]);
158 printf("%d\n" , t == N + 1 ? -1 : t);
159 }
160 return 0;
161 }