Codeforces 1606F - Tree Queries(虚树+树形 dp)
显然我们选择删除的点连同 \(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 序,然后直接在 set 中 lower_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)的更多相关文章
- 【BZOJ-3572】世界树 虚树 + 树形DP
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1084 Solved: 611[Submit][Status ...
- 【BZOJ-2286】消耗战 虚树 + 树形DP
2286: [Sdoi2011消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2120 Solved: 752[Submit][Status] ...
- bzoj 2286(虚树+树形dp) 虚树模板
树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5002 Sol ...
- BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca
BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...
- [WC2018]通道——边分治+虚树+树形DP
题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...
- 2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)
传送门 虚树入门题? 好难啊. 在学习别人的写法之后终于过了. 这道题dp方程很好想. 主要是不好写. 简要说说思路吧. 显然最优值只能够从子树和父亲转移过来. 于是我们先dfs一遍用儿子更新父亲,然 ...
- CodeCraft-19 and Codeforces Round #537 (Div. 2) E 虚树 + 树形dp(新坑)
https://codeforces.com/contest/1111/problem/E 题意 一颗有n个点的树,有q个询问,每次从树挑出k个点,问将这k个点分成m组,需要保证在同一组中不存在一个点 ...
- Codeforces 1111 E. Tree(虚树,DP)
题意 有一棵树,q个询问,每次询问,指定一个点做树根,再给定k个点,要求把这些点分成不超过m组的方案数,分配的限制是任意两个有祖先关系的点不能分在同一组.题目还保证了所有的询问的k加起来不超过1e5. ...
随机推荐
- Golang通脉之基础入门
为什么要学 Go 性能优越感:Go 极其地快,其性能与 Java 或 C++相似.在使用中,Go 一般比 Python 要快 30 倍: 序列化/去序列化.排序和聚合中表现优异: 开发者效率较高:多种 ...
- 4.1 The Example Domain 领域示例代码
4.1 The Example Domain 领域示例代码 The examples will use some concepts those are used by GitHub, like Iss ...
- 【UE4 C++】Print、Delay、ConsoleCommand
基于UKismetSystemLibrary PrintString /** * Prints a string to the log, and optionally, to the screen * ...
- 力扣 - 剑指 Offer 53 - I. 在排序数组中查找数字 I
题目 剑指 Offer 53 - I. 在排序数组中查找数字 I 思路1 一般来说,首先想到的是使用一个变量,从头开始遍历整个数组,记录target数组出现的次数,但是这样的时间复杂度是O(n),还是 ...
- linux系统上国际化失败
文章目录 一.需求: 二.出现的问题 三.代码结构 1.配置文件中的配置 2.java代码中的使用 四.解决方案 一.需求: 最近项目中有这么一个需求,当用户当前的语言环境是 中文时,导出的 exce ...
- vs2010中release模式下调试程序
debug模式调试信息全,但是速度很慢,在数据量比较大的时候非常影响调试效率,release模式速度快,但是没有调试信息.所以在编译的时候很多编译器会提供一种折中的编译方式,在release下提供调试 ...
- [hi3521] nand flash 的 boot 启动模式的区别?
spi nand flash 的 boot 启动模式选择.0:1 线 boot:1:4 线 boot.请问,1线boot和4线boot有什么区别呢?该如何选择呢? 收藏 顶 踩 回复 使用 ...
- 通过串口利用printf函数输出数据
一.printf函数格式 printf函数具有强大的输出功能 %表示格式化字符串输出 目前printf支持以下格式的输出,例如: printf("%c",a);输出单个字符. pr ...
- 在c中使用正则表达式
今天学习编译原理的时候,用c写一个简易的文法识别器实验遇到了一个问题:要用正则表达式去识别正则文法里面的A->ω,A->Bω, 其中ω属于T的正闭包,也就是说我们对正则文法的产生式进行抽象 ...
- poj 2724 Purifying Machine(二分图最大匹配)
题意: 有2^N块奶酪,编号为00...0到11..1. 有一台机器,有N个开关.每个开关可以置0或置1,或者置*.但是规定N个开关中最多只能有一个开关置*. 一旦打开机器的开关,机器将根据N个开关的 ...