复习1-树上问题
A. 闹市
- 在树上做差分, 求lca 最后再跑一遍dfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65const int N = 1e6+10;
int n,k;
struct{
int to,nex;
}edge[N<<1];
int head[N<<1],edge_num;
inline void add(int x,int y){
edge[++edge_num].to = y;
edge[edge_num].nex = head[x];
head[x] = edge_num;
}
int fa[N][25],dep[N];
inline void dfs(int now,int fu){
fa[now][0] = fu;
dep[now] = dep[fu]+1;
for(int i=1;(1<<i)<=dep[now];++i) fa[now][i] = fa[fa[now][i-1]][i-1];
for(int i=head[now];i;i=edge[i].nex){
int tto = edge[i].to;
if(tto==fu) continue;
dfs(tto,now);
}
}
inline int lca(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
for(int i=24;i>=0;--i){
if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];
}
if(x==y) return x;
for(int i=24;i>=0;--i){
if(fa[y][i]!=fa[x][i]){
y = fa[y][i];
x = fa[x][i];
}
}
return fa[x][0];
}
int tmp[N],ans;
inline void dfs1(int now,int fu){
for(int i=head[now];i;i=edge[i].nex){
int tto = edge[i].to;
if(tto==fu) continue;
dfs1(tto,now);
tmp[now] += tmp[tto];
}
ans = max(ans,tmp[now]);
}
int main(){
read(n,k);
for(int i=1,u,v;i<n;++i){
read(v,u);
add(u,v);
add(v,u);
}
dfs(1,0);
for(int i=1,u,v;i<=k;++i){
read(u,v);
tmp[u]++;tmp[v]++;tmp[lca(u,v)]--;tmp[fa[lca(u,v)][0]]--;
}
dfs1(1,0);
printf("%d",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
B. 王子
- dfs序,用深度去维护
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26using namespace std;
const int N=2e5+5;
int n,m,a[N],cnt[N],sz[N],ans[N];
vector<int> E[N];
vector<pair<int,int> > q[N];
void dfs(int u,int d,int f){
for(auto x:q[u]) ans[x.first]=cnt[d+x.second+1];
sz[u]=a[u];
for(auto v:E[u]) if(v!=f) dfs(v,d+1,u),sz[u]+=sz[v];
cnt[d]+=sz[u];
for(auto x:q[u]) ans[x.first]+=sz[u]-cnt[d+x.second+1];
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=2;i<=n;i++){
int u;scanf("%d",&u);
E[u].push_back(i);E[i].push_back(u);
}
for(int i=1;i<=m;i++){
int x,h;scanf("%d%d",&x,&h);
q[x].push_back({i,h});
}
dfs(1,0,0);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
C. 逃跑
- dfs栈 + 二分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using namespace std;
const int N = 2e5+5;
int n,m,dis[N],sum[N],fa[N],tot;
pair<int,int>s[N];
vector<pair<int,int> >g[N];
void dsy(int u){
s[++tot]={dis[u],u};
sum[u]++;
auto tmp=lower_bound(s+1,s+1+tot,make_pair(dis[u]-m,0ll));
sum[fa[tmp->second]]--;
for(auto i : g[u]){
int v=i.first,w=i.second;
dis[v]=dis[u]+w;
fa[v]=u;
dsy(v);
}
tot--;
}
void ysd(int u){
for(auto i : g[u]){
int v=i.first;
ysd(v);
sum[u]+=sum[v];
}
}
signed main(){
cin>>n>>m;
for(int i = 2;i <= n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(make_pair(i,v));
}
dsy(1);
ysd(1);
for(int i = 1;i <= n;i++)cout<<sum[i]<<'\n';
return 0;
}
D. 树上最多不相交路径
- 按照LCA深度贪心选取
- 满分的贪心,考虑一个性质:两条路径的相交,一定是某一条路径的lca在另一条路径上。
选择一条路径之后,它的lca对应的子树中的其他点,是不能在通过lca走向其他的点。
优先选取lca深度最大的点。这样影响的路径最少。
选取一条路径之后,把它lca内的所有节点都标记掉,表示不可以在使用。一个点如果已经被标记了,就不用在继续往下标记了。这样每个点,只会被标记一次。
1 | const int N=2e5+10; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ysmmm的快乐小屋!