[HNOI2014][bzoj3572] 世界树 [虚树+dp]

题面:

传送门

思路:

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

但是我认为这道题目出的难点和亮点不在于虚树,而在于建出虚树以后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();
}
上一篇:WEB接口测试之Jmeter接口测试自动化 (二)


下一篇:FZU Problem 2148 Moon Game (判断凸四边形)