UOJ #11 - 【UTR #1】ydc的大树(换根 dp)
Emmm……这题似乎做法挺多的,那就提供一个想起来写起来都不太困难的做法吧。
首先不难想到一个时间复杂度 \(\mathcal O(n^2)\) 的做法:对于每个黑点我们以它为根求出离它距离最远的点集 \(S\),那么一个白点能够摧毁这个黑点当且仅当这个白点在黑点到点集 \(S\) 中的点的 \(\text{LCA}\) 的路径上。这样我们就可以求出所有白点的答案了。
考虑优化这个过程,注意“以每个点为根”一脸可用换根 \(dp\) 优化的亚子,因此考虑换根 \(dp\),如果单纯地求到每个点距离最远的黑点那你肯定会求的欸(yyq 附体),直接一遍常规的换根 \(dp\) 就完事了。不过此题还需求 \(\text{LCA}\),因此考虑以下做法,我们先以 \(1\) 为根一遍 DFS,对于每个点 \(x\) 求出以 \(x\) 为根的子树内里 \(x\) 最远的点的 \(\text{LCA}\),然后我们再额外记录两个数组 \(dis\_out_x\) 表示去掉以 \(x\) 为根的子树内剩余部分离 \(x\) 最远的黑点离 \(x\) 的距离,以及 \(lca\_out_i\) 表示它们的 \(\text{LCA}\),怎样求这两个数组呢?就按照第二遍 DFS 的套路从上往下更新,当 DFS 到 \(x\) 时将 \(x\) 的每个子树的信息压入一个 multiset
,并枚举 \(x\) 的每个儿子 \(y\),将 \(y\) 的信息从 multiset
中暂时删除,如果 multiset
中距离最大值和次大值相等那么 \(lca\_out_y\) 就是 \(x\),否则 \(lca\_out_y\) 就是 multiset
中最大值对应的 \(\text{LCA}\),最后更新答案时对于每个 \(x\) 看它子树内和子树外黑点距离其的最大值,哪边大那个 \(\text{LCA}\) 就属于那边,如果相等那么 \(\text{LCA}\) 就是 \(x\)。
最后统计答案树上差分即可,时间复杂度 \(n\log n\)。
const int MAXN=1e5;
const int LOG_N=17;
const int INF=0x3f3f3f3f;
int n,m,hd[MAXN+5],to[MAXN*2+5],val[MAXN*2+5],nxt[MAXN*2+5],ec=0;bool is[MAXN+5];
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
int fa[MAXN+5][LOG_N+2],dep[MAXN+5],dis[MAXN+5],lca_in[MAXN+5];
int dis_out[MAXN+5],lca_out[MAXN+5];
int mark[MAXN+5];
void dfs1(int x=1,int f=0){
dis[x]=(is[x]^1)*(-INF);lca_in[x]=x;fa[x][0]=f;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dep[y]=dep[x]+1;dfs1(y,x);
if(dis[y]+z>dis[x]) dis[x]=dis[y]+z,lca_in[x]=lca_in[y];
else if(dis[y]+z==dis[x]) lca_in[x]=x;
} /*printf("%d %d %d\n",x,dis[x],lca_in[x]);*/
}
int getlca(int x,int y){
if(dep[x]<dep[y]) x^=y^=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];
}
void dfs2(int x=1,int f=0){
multiset<pii> st;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];
if(y==f) st.insert(mp(dis_out[x],lca_out[x]));
else st.insert(mp(dis[y]+z,lca_in[y]));
} if(is[x]) st.insert(mp(0,x));
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
st.erase(st.find(mp(dis[y]+z,lca_in[y])));
if(st.empty()) dis_out[y]=(is[x])?z:(-INF),lca_out[y]=(is[x])?x:0;
else{
// printf("Node %d\n",y);
pii p=*st.rbegin();st.erase(st.find(p));dis_out[y]=z+p.fi;
// printf("%d %d\n",p.fi,p.se);
if(st.empty()||(*st.rbegin()).fi<p.fi) lca_out[y]=p.se;
else lca_out[y]=x;st.insert(p);
} st.insert(mp(dis[y]+z,lca_in[y]));dfs2(y,x);
}
}
void dfs3(int x=1,int f=0){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dfs3(y,x);mark[x]+=mark[y];
}
}
int main(){
scanf("%d%d",&n,&m);for(int i=1,x;i<=m;i++) scanf("%d",&x),is[x]=1;
for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),adde(u,v,w),adde(v,u,w);dis_out[1]=-INF;
dfs1();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];dfs2();
for(int i=1;i<=n;i++) if(is[i]){
int y=(dis[i]==dis_out[i])?i:((dis[i]<dis_out[i])?lca_out[i]:lca_in[i]);
// printf("%d %d %d %d\n",i,dis[i],dis_out[i],y);
int z=getlca(i,y);mark[i]++;mark[y]++;mark[z]--;mark[fa[z][0]]--;
} dfs3();int mx=0,cnt=0;
for(int i=1;i<=n;i++) if(!is[i]) chkmax(mx,mark[i]);
for(int i=1;i<=n;i++) cnt+=(!is[i]&&mark[i]==mx);
printf("%d %d\n",mx,cnt);
return 0;
}
UOJ #11 - 【UTR #1】ydc的大树(换根 dp)的更多相关文章
- Codeforces 891D - Sloth(换根 dp)
Codeforces 题面传送门 & 洛谷题面传送门 换根 dp 好题. 为啥没人做/yiw 首先 \(n\) 为奇数时答案显然为 \(0\),证明显然.接下来我们着重探讨 \(n\) 是偶数 ...
- [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]
题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...
- 2018.10.15 NOIP训练 水流成河(换根dp)
传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- 小奇的仓库:换根dp
一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...
- 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)
题意 题目链接:https://www.luogu.org/problem/P4827 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...
- Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)
题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...
- bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp
题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...
- codeforces1156D 0-1-Tree 换根dp
题目传送门 题意: 给定一棵n个点的边权为0或1的树,一条合法的路径(x,y)(x≠y)满足,从x走到y,一旦经过边权为1的边,就不能再经过边权为0的边,求有多少边满足条件? 思路: 首先,这道题也可 ...
随机推荐
- 初识HTML02
HTML 超文本标记语言 什么是超文本标记语言 浏览器能够解释和解析的语言 通过元素的形式构建页面结构和填充内容 构建HTML页面 构建页面的步骤 创建一个扩展名为.html和.html的页面文件 向 ...
- Python在Linux下编译安装报错:Makefile:1141:install
正常情况下执行:./configuremake && make install可以直接安装python,但是在在更新了乌版图后居然报错了!!!检查了一圈,发现乌版图安装了python3 ...
- F1英国大奖赛-银石赛道地图及弯道
背景 今天晚上(2020-08-02)是今年英国大奖赛的正赛.刚好了解了一下赛道地图.记录一下,明年就不用到处找了. 简介 银石赛道(Silverstone Circuit)由一个废弃的空军基地改建, ...
- Servlet学习一(Servlet的使用流程)
一.servlet运行流程 运行流程:浏览器发送请求到服务器,服务器根据url地址在webapps中寻找对应的项目文件夹然后再web.xml中检索对应的servlet,并进行调用二.servlet类写 ...
- Scrum Meeting 0503
零.说明 日期:2021-5-3 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成登录.后端管理 ...
- 热身训练4 Eighty seven
Eighty seven 简要题意: n个卡片,其中第i个卡片的数值为$a[i]$.一共q次询问,每次询问将删去其中3个卡片(可能删除若干相同的卡片)后,问能否选出10个卡片,数值之和等于87. n≤ ...
- NKOJ1828 Feed Ratios饲料调配
题目 好题!高斯消元切了! (其实只是单纯地想吐槽这道出现在"高斯消元"专练里的题,暴搜能过,goudoubuxie"Gauss") 下面是暴搜: #pragm ...
- USART波特率 vs SPI速率--学习笔记
本篇文章将与大家探讨USART波特率 vs SPI速率.这里提出一个问题,为什么USART的波特率是内核时钟的1/8或者1/16,而SPI最快的频率可以是内核时钟的1/2. 请大家带着这个问题来阅读本 ...
- 『学了就忘』Linux基础 — 9、虚拟机中快照的使用
目录 1.快照的含义 2.快照的使用 步骤一:创建拍摄快照 步骤二:填写快照信息并创建 步骤三:查看快照 步骤四:操作快照 3.管理虚拟机小技巧 4.关于快照说明 快照和克隆是VMware中两个非常实 ...
- stat命令的实现
任务详情 学习使用stat(1),并用C语言实现 提交学习stat(1)的截图 man -k ,grep -r的使用 伪代码 产品代码 mystate.c,提交码云链接 测试代码,mystat 与st ...