Codeforces 题面传送门 & 洛谷题面传送门

显然我们选择删除的点连同 \(u\) 会形成一个连通块,否则我们如果选择不删除不与 \(u\) 在同一连通块中的点,答案一定更优。

注意到如果我们选择删除 \(u\) 的某个儿子 \(v\),那么答案的增量为 \(chd_v-1-k\),其中 \(chd_v\) 为节点 \(v\) 儿子的个数。而初始时刻答案为 \(chd_u\) 是个定值,因此我们的任务可以等效于,给每个点赋上一个点权 \(chd_u-1-k\),然后要找到一个以 \(u\) 为根的树上连通块,使得这个连通块中除去点 \(u\) 之外其他点点权之和尽可能大。

因此我们有了一个复杂度 \(\Theta(n^2)\) 的做法:对于每一个 \(k\) 都跑一遍树形 \(dp\),即设 \(dp_u\) 表示以 \(u\) 为根的连通块点权之和的最大值,那么显然有转移 \(dp_u=chd_u-1-k+\sum\limits_{v\in son_u}\max(dp_v,0)\)。注意到我们选出的树上连通块边界上(也就是所有满足 \(x\) 在树上连通块中,但 \(x\) 的所有儿子都不在树上连通块)中的点的点权必定都是正的,否则我们扣掉那些点权非正且在连通块边界上的点,答案肯定变得更优。因此考虑对于每个 \(k\) 将所有点权为正的点拎出来建一棵虚树——由于 \(\sum\limits_{i=1}^nchd_i=n-1\),所以对于所有 \(k\),点权为正的点的总个数是 \(\Theta(n)\) 的,然后对它们跑树形 \(dp\),然后每次查询一个点 \(u\) 为根的连通块点权之和的最大值时,如果点 \(u\) 本身就在虚树上,直接返回 \(u\) 的 DP 值即可,否则我们找到 \(u\) 在虚树上所在的链下方的节点 \(v\)——这个可以拿个 set 存储所有在虚树上的节点的 DFS 序,然后直接在 setlower_bound。那么以 \(u\) 为根的连通块点权和的最大值就是 \(dp_v\) 加上 \(fa_v\to u\) 这段路径和的点权和。

时间复杂度 \(\Theta(n\log n)\)​​。

const int MAXN=2e5;
const int LOG_N=18;
int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int chd[MAXN+5],dep[MAXN+5],fa[MAXN+5][LOG_N+2];
int dfn[MAXN+5],tim=0,rid[MAXN+5],edt[MAXN+5];
void dfs0(int x,int f){
fa[x][0]=f;rid[dfn[x]=++tim]=x;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
chd[x]++;dep[y]=dep[x]+1;dfs0(y,x);
} edt[x]=tim;
}
int sum_chd[MAXN+5];
void dfs_chd(int x,int f){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
sum_chd[y]=sum_chd[x]+chd[y];
dfs_chd(y,x);
}
}
void lca_init(){
dep[1]=1;dfs0(1,0);
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int getlca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=LOG_N;~i;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=LOG_N;~i;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
vector<int> pos[MAXN+5];
int stk[MAXN+5],tp=0;set<int> st;
vector<pii> qv[MAXN+5];
ll calc_sum(int u,int v,int k){//u is ancestor of v
return (sum_chd[v]-sum_chd[u])-1ll*(k+1)*(dep[v]-dep[u]);
}
int calc_val(int u,int k){return chd[u]-1-k;}
vector<int> g[MAXN+5];
ll dp[MAXN+5],res[MAXN+5];
void insert(int x){
if(!tp) return stk[++tp]=x,void();
int lc=getlca(x,stk[tp]);
// printf("LCA of %d %d is %d\n",x,stk[tp],lc);
// printf("stack: ");
// for(int i=1;i<=tp;i++) printf("%d%c",stk[i]," \n"[i==tp]);
while(tp>1&&dep[stk[tp-1]]>dep[lc]){
// printf("adde %d %d\n",stk[tp-1],lc);
g[stk[tp-1]].pb(stk[tp]);--tp;
}
if(tp&&dep[stk[tp]]>dep[lc]){
// printf("adde %d %d\n",lc,stk[tp]);
g[lc].pb(stk[tp--]);
}
if(!tp||stk[tp]!=lc) stk[++tp]=lc;
stk[++tp]=x;
}
void fin(){
while(tp>=2){
// printf("adde %d %d\n",stk[tp-1],stk[tp]);
g[stk[tp-1]].pb(stk[tp]);--tp;
} tp=0;
}
void dfs_dp(int x,int k){
dp[x]=calc_val(x,k);st.insert(dfn[x]);
for(int y:g[x]){
dfs_dp(y,k);
dp[x]+=max(dp[y]+calc_sum(x,y,k)-calc_val(y,k),0ll);
// printf("calc_sum %d %d %d = %lld\n",x,y,k,calc_sum(x,y,k));
} //printf("DP %d %lld\n",x,dp[x]);
}
void clr(int x){for(int y:g[x]) clr(y);g[x].clear();}
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
lca_init();sum_chd[1]=chd[1];dfs_chd(1,0);scanf("%d",&qu);
// for(int i=1;i<=n;i++) printf("chd[%d]=%d\n",i,chd[i]);
for(int i=1;i<=n;i++) for(int j=0;j<chd[i];j++) pos[j].pb(i);
for(int i=1,u,k;i<=qu;i++) scanf("%d%d",&u,&k),qv[k].pb(mp(u,i));
for(int i=0;i<=MAXN;i++){
st.clear();
if(!pos[i].empty()){
// printf("solving %d\n",i);
sort(pos[i].begin(),pos[i].end(),[&](int x,int y){return dfn[x]<dfn[y];});
if(pos[i][0]!=1) insert(1);
for(int x:pos[i]) insert(x);
fin();dfs_dp(1,i);
}
for(pii p:qv[i]){
int u=p.fi,id=p.se;
set<int>::iterator it=st.lower_bound(dfn[u]);
if(it==st.end()||(*it)>edt[u]) res[id]=chd[u];
else{
int pt=rid[*it];
// printf("%d %d\n",id,pt);
// printf("%lld\n",calc_sum(fa[u][0],fa[pt][0],i));
res[id]=chd[u]+max(0ll,dp[pt]+calc_sum(fa[u][0],fa[pt][0],i)-calc_val(u,i));
}
}
clr(1);
}
for(int i=1;i<=qu;i++) printf("%lld\n",res[i]);
return 0;
}
/*
11
1 2
2 3
2 4
1 5
5 6
5 7
7 8
7 9
7 10
1 11
4
1 0
1 1
5 2
11 0
*/

Codeforces 1606F - Tree Queries(虚树+树形 dp)的更多相关文章

  1. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  2. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  3. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  4. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  5. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  6. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  7. 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)

    传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...

  8. CodeCraft-19 and Codeforces Round #537 (Div. 2) E 虚树 + 树形dp(新坑)

    https://codeforces.com/contest/1111/problem/E 题意 一颗有n个点的树,有q个询问,每次从树挑出k个点,问将这k个点分成m组,需要保证在同一组中不存在一个点 ...

  9. Codeforces 1111 E. Tree(虚树,DP)

    题意 有一棵树,q个询问,每次询问,指定一个点做树根,再给定k个点,要求把这些点分成不超过m组的方案数,分配的限制是任意两个有祖先关系的点不能分在同一组.题目还保证了所有的询问的k加起来不超过1e5. ...

随机推荐

  1. windows右键菜单自动打包发布nuget,没有CI/CD一样方便!

    构建现代的 .Net 应用离不开 Nuget 的支持,而快速打包 Nuget 成了提高生产率的有效方法.没有CI/CD?来试试使用windows右键菜单吧 先看右键效果图 有时候我们可能没有CI/CD ...

  2. Sequence Model-week3编程题1-Neural Machine Translation with Attention

    1. Neural Machine Translation 下面将构建一个神经机器翻译(NMT)模型,将人类可读日期 ("25th of June, 2009") 转换为机器可读日 ...

  3. djago后台管理页面

    from django.contrib import admin from blogtest.models import * #修改网页title和站点header.+ admin.site.site ...

  4. LeetCode:BFS/DFS

    BFS/DFS 在树专题和回溯算法中其实已经涉及到了BFS和DFS算法,这里单独提出再进一步学习一下 BFS 广度优先遍历 Breadth-First-Search 这部分的内容也主要是学习了labu ...

  5. Java:死锁编码及定位分析

    Java:死锁编码及定位分析 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 概念 死锁是指两个或多个以上的进程在执行过程中,因争夺资源而造成一种互相等待的现象, ...

  6. Java:LinkedList类小记

    Java:LinkedList类小记 对 Java 中的 LinkedList类,做一个微不足道的小小小小记 概述 java.util.LinkedList 集合数据存储的结构是循环双向链表结构.方便 ...

  7. oo第二次博客-三次电梯调度的总结与反思

    本单元从电梯调度相关问题层层深入,带领我们学习并运用了了多线程相关的知识. 三次电梯调度依次为单电梯单容量.单电梯可携带.多电梯可携带. 一.我的设计 在第一次作业中,使用了最简单的FIFO调度方法. ...

  8. 关于麦克风的参数介绍 - 驻极体麦克风(ECM)和硅麦(MEMS)

    1.麦克风的分类1.1.动圈式麦克风(Dynamic Micphone)原理:基本构造包含线圈.振膜.永久磁铁三部分.当声波进入麦克风,振膜受到声波的压力而产生振动,与振膜在一起的线圈则开始在磁场中移 ...

  9. MiniFly四轴飞行器之部分系统及电源分析

    最近硬件四轴很火,了解了很久,还是选择了MiniFly,主要还是资料多,后边可以有人讨论,不像很多就是建了个群,研究问题还是在论坛方便很多. 四轴终于拿到手,功能很强大,主要是还支持二次开发,可以研究 ...

  10. SpringCloud微服务实战——搭建企业级开发框架(九):使用Nacos发现、配置和管理微服务

    Nacos是一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台,Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置 ...