题面:

传送门

思路:

一道虚树的好题,是很多虚树博客的入门题目

但是我认为这道题目出的难点和亮点不在于虚树,而在于建出虚树以后dp的思路与实现

下文中为方便描述,用势力范围来表示一个“议事处”管辖的节点集合

首先,看完题目以后一个直观的感受,就是我去考虑两个询问点之间,哪些点属于谁的势力范围,然后这样

这是个类似暴力的东西

那树上路径的中点怎么求?可以lca求出距离以后倍增

但是现在问题来了,虚树上的节点有的是询问点,有的只是询问点的lca,并不参与询问,怎么解决这个问题呢?

而且在那个初始想法中,有一些路径是势必会重复的,又怎么解决?

一个直观的想法就是把一堆路径询问简化掉

我们发现一个点终究只能属于一个询问点的势力范围,那么一条边上的节点同理

所以我们可以把重复经过同一条边的询问拆到每条虚树边上,然后对于每条虚树边来考虑解题

显然,虚树边分为两种:两边的虚树点被同一个节点控制的,或者两端被不同节点控制的

对于两端被同一个询问点控制的边,显然这条虚树边上的所有的点的所有子树都被这个点控制

另一种情况则是在中间有一个分界线,界线一边的点与它们的子树被那一边的端点的“主人”控制

那么也就是说,我们只需要处理出虚树上的每一个节点被哪个询问点控制,然后对于上面的两种情况分别考虑、统计答案就行了

那么怎么处理这个控制关系呢?

我们做两次dfs

第一次,我们求出某个虚树节点的所有儿子中离它最近的控制点

第二次,则用父亲的控制点(和儿子不在同一个子树中)来更新儿子的控制点

dp的时候则是依然用倍增来实现跳到分界点上

两个点之间的距离则可以用lca实现

大概就是这样了,更具体的内容可以参考代码

Code:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
inline int read(){
int re=,flag=;char ch=getchar();
while(ch>''||ch<''){
if(ch=='-') flag=-;
ch=getchar();
}
while(ch>=''&&ch<='') re=(re<<)+(re<<)+ch-'',ch=getchar();
return re*flag;
}
int n,dep[],siz[],st[][],dfn[],clk;
struct graph{
int first[],cnt;
struct edge{
int to,next;
}a[];
inline void add(int u,int v){
if(u==v) return;
a[++cnt]=(edge){v,first[u]};first[u]=cnt;
}
graph(){
memset(first,-,sizeof(first));cnt=;
}
void init(){cnt=;}
}G,g;
void dfs(int u,int f){
// cout<<"begin dfs "<<u<<endl;
int i,v;
dep[u]=dep[f]+;st[u][]=f;dfn[u]=++clk;siz[u]=;
for(i=G.first[u];~i;i=G.a[i].next){
v=G.a[i].to;
if(v==f) continue;
dfs(v,u);siz[u]+=siz[v];
}
// cout<<"end of dfs "<<u<<ends<<dep[u]<<ends<<siz[u]<<endl;
return;
}
void ST(){
int i,j;
for(j=;j<=;j++) for(i=;i<=n;i++) st[i][j]=st[st[i][j-]][j-];
}
int lca(int l,int r){
if(dep[l]>dep[r]) swap(l,r);
int i;
for(i=;i>=;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
if(l==r) return l;
for(i=;i>=;i--)
if(st[r][i]!=st[l][i]){
r=st[r][i];
l=st[l][i];
}
return st[l][];
}
int q[],ans[],num[],s[],top;
bool vis[];
bool cmp1(int l,int r){
return dfn[l]<dfn[r];
}
bool cmp2(int l,int r){
return num[l]<num[r];
}
int minn[],belong[],sur[];
int dis(int l,int r){return dep[l]+dep[r]-*dep[lca(l,r)];}
void dfs1(int u){
int i,v,d1,d2;belong[u]=(vis[u]?u:);sur[u]=siz[u];
for(i=g.first[u];~i;i=g.a[i].next){
v=g.a[i].to;dfs1(v);
d1=dep[belong[v]]-dep[u];d2=(belong[u]?dep[belong[u]]-dep[u]:inf);
if(d1<d2||(d1==d2&&belong[v]<belong[u])) belong[u]=belong[v];
}
}
void dfs2(int u){
int i,v,d1,d2;
for(i=g.first[u];~i;i=g.a[i].next){
v=g.a[i].to;d1=dis(belong[u],v);d2=dis(belong[v],v);
if(d1<d2||(d1==d2&&belong[u]<belong[v])) belong[v]=belong[u];
dfs2(v);
}
}
void dp(int u){
// cout<<"enter dp "<<u<<ends<<belong[u]<<endl;
int i,v,d1,d2,mid,tv,j,tmp;
for(i=g.first[u];~i;i=g.a[i].next){
v=g.a[i].to;g.first[u]=g.a[i].next;mid=tv=v;
// cout<<"going to "<<v<<endl;
dp(v);
for(j=;j>=;j--) if(dep[st[tv][j]]>dep[u]) tv=st[tv][j];
sur[u]-=siz[tv];
// cout<<"edge up to "<<tv<<endl;
if(belong[u]==belong[v]){
ans[belong[u]]+=siz[tv]-siz[v];continue;
}
for(j=;j>=;j--){
tmp=st[mid][j];if(dep[tmp]<=dep[u]) continue;
d1=dis(tmp,belong[v]);d2=dis(tmp,belong[u]);
if(d1<d2||(d1==d2&&belong[v]<belong[u])) mid=tmp;
}
// cout<<"********diff edge "<<mid<<endl;
ans[belong[u]]+=siz[tv]-siz[mid];
ans[belong[v]]+=siz[mid]-siz[v];
}
// cout<<"end of dp "<<u<<ends<<sur[u]<<endl;
ans[belong[u]]+=sur[u];
}
void solve(){
// cout<<"*************************enter solve\n";
int m=read(),i,grand;
for(i=;i<=m;i++) q[i]=read(),num[q[i]]=i,vis[q[i]]=;;
sort(q+,q+m+,cmp1);top=;g.init();s[++top]=;
for(i=;i<=m;i++){
// if(!top){s[++top]=q[i];continue;}
grand=lca(s[top],q[i]);
while(){
if(dep[s[top-]]<=dep[grand]){
g.add(grand,s[top--]);
if(s[top]!=grand) s[++top]=grand;
break;
}
g.add(s[top-],s[top]);top--;
}
if(s[top]!=q[i]) s[++top]=q[i];
}
while(--top>=) g.add(s[top],s[top+]);
dfs1(s[]);dfs2(s[]);dp(s[]);
sort(q+,q+m+,cmp2);
// for(i=1;i<=m;i++) cout<<q[i]<<ends;cout<<endl;
for(i=;i<=m;i++) vis[q[i]]=,num[q[i]]=inf,printf("%d ",ans[q[i]]),ans[q[i]]=;
puts("");
}
int main(){
// cout<<"begin\n";
int i,t1,t2;
n=read();memset(num,0x3f,sizeof(num));
// cout<<"input n "<<n<<endl;
for(i=;i<n;i++){
t1=read();t2=read();
G.add(t1,t2);G.add(t2,t1);
}
// cout<<"input of tree end\n";
dfs(,);ST();
int Q=read();
for(i=;i<=Q;i++) solve();
}

[HNOI2014][bzoj3572] 世界树 [虚树+dp]的更多相关文章

  1. BZOJ 3572 [HNOI2014]世界树 (虚树+DP)

    题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...

  2. bzoj 3572世界树 虚树+dp

    题目大意: 给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制 求每个关键点控制多少个点 分析: 虚树+dp dp过程如下: 第一次dp,递归求出每个点子树中 ...

  3. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  4. BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

    传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...

  5. 洛谷 P3233 [HNOI2014]世界树(虚树+dp)

    题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...

  6. 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增

    [题意]给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点.n,m,Σki<=300000. [算法]虚树+倍增 [题解]★参考:thy_asd ...

  7. bzoj 2286 [Sdoi2011]消耗战 虚树+dp

    题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...

  8. 【BZOJ】2286: [Sdoi2011]消耗战 虚树+DP

    [题意]给定n个点的带边权树,每次询问给定ki个特殊点,求隔离点1和特殊点的最小代价.n<=250000,Σki<=500000. [算法]虚树+DP [题解]考虑普通树上的dp,设f[x ...

  9. [BZOJ5287][HNOI2018]毒瘤(虚树DP)

    暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...

随机推荐

  1. make 出错: /usr/bin/ld: cannot find -lrt

    make 出错:/usr/bin/ld: cannot find -lrtcollect2: ld returned 1 exit statusmake: *** [page_parser] Erro ...

  2. SQL小知识_长期总结

    1. 左联接右联接区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录inner ...

  3. IDEA搭建Maven 的聚合项目

    今天突然想把自己学习在eclipse上的maven聚合项目搭建到IDEA上,结果IDEA有太多的配置步骤,导致失败了很多次,终于在网上找到了一篇博客 https://blog.csdn.net/for ...

  4. 精致的系统监控工具-netdata

    今天在网上瞎逛,偶然发现一款监控工具:netdata,感到一惊,监控工具竟然可以这么漂亮! 简单了解一下,这款工具还算比较新,监控系统运行状态的功能非常强大,除了监控cpu,网卡,磁盘,内存,进程等等 ...

  5. DC84问

    1.1 什么是DC?DC(Design Compiler)是Synopsys公司的logical synthesis工具,它根据design description和design constraint ...

  6. HDU - 1973 - Prime Path (BFS)

    Prime Path Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  7. golang echo livereload

    echo on port 1323 gin -a 1323 run server.go go get github.com/codegangsta/gin gin -h

  8. Java语言基础---逻辑运算(长路短路运算)

    长路短路运算的区别 长路与运算&:是指在两边都是整数时,是逐位与运算,在两边是关系运算时,是逻辑运算. 短路与运算&&:是指从左至右,遇到false,则停止后面的运算. 长路或 ...

  9. How to check if Visual Studio 2005 SP1 is installed

    How to check if Visual Studio 2005 SP1 is installed Check the following registry key. HKEY_LOCAL_MAC ...

  10. wim

    wim 编辑 WIM是英文Microsoft Windows Imaging Format(WIM)的简称,它是Windows基于文件的映像格式.WIM 映像格式并非现在相当常见的基于扇区的映像格式, ...