BZOJ 3572 [HNOI2014]世界树 (虚树+DP)
题目大意:略
细节贼多的虚树$DP$
先考虑只有一次询问的情况
一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖
跑两次$dfs$即可,时间$O(n)$
再考虑一条链的情况
一条链上有很多个特殊点,相邻两个特殊点$x,y$之间可能有很多连续的非特殊点,那么在这些连续的非特殊点上会有一个分界,前面一部分被$x$管辖,后面一部分被$y$管辖
在链上二分即可,时间$O(mlogm)$
正解就是把上面两种情况结合起来..用虚树维护一下
首先根据套路对特殊点建出虚树,虚树上会出现所有的特殊点以及一些作为$LCA$的非特殊点。
用第一种情况的方法在虚树上搜索一遍,求出虚树上的每个节点被哪些节点管辖
再考虑剩余节点的贡献
对于虚树上相邻的两个节点$x,y$,假设$dep[x]<dep[y]$,我们取出原树上端点为$x,y$的链$F$,然后把$F$的两个端点$x,y$去掉,贡献分为两种情况
$x,y$被同一个的节点管辖,那么链F上的节点以及链F上挂着的子树也都会被这个节点管辖
$x,y$被不同的节点管辖,借用第二种情况的方法,链F上一定存在一个分界点,上面一部分被管辖x的节点管辖,下面一部分被管辖y的节点管辖,倍增跳一下即可
看起来很好写,实际上细节真的不少啊..上午迷迷糊糊写+调了4h才过
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define N1 300100
using namespace std;
const int inf=0x3f3f3f3f; template <typename _T> void read(_T &ret)
{
ret=; _T fh=; char c=getchar();
while(c<''||c>''){ if(c=='-') fh=-; c=getchar(); }
while(c>=''&&c<=''){ ret=ret*+c-''; c=getchar(); }
ret=ret*fh;
}
template <typename _T> _T chkmin(_T &x,_T &y,_T vx,_T vy)
{
if(vx<vy) return x;
if(vx>vy) return y;
return x<y?x:y;
} struct Edge{
int to[N1*],nxt[N1*],head[N1],cte;
void ae(int u,int v)
{ cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; } //val[cte]=w;
void clr()
{ memset(to,,(cte+)*); memset(nxt,,(cte+)*); cte=; }
}e,g; int n,de;
int lg[N1*];
int dep[N1],ff[N1][],eu[N1*][],st[N1],sz[N1],cur; void dfs(int x)
{
int j,v; ff[x][]=x; eu[st[x]=++cur][]=x;
for(j=e.head[x];j;j=e.nxt[j])
{
v=e.to[j]; if(v==ff[x][]) continue;
ff[v][]=x; dep[v]=dep[x]+;
dfs(v);
eu[++cur][]=x; sz[x]+=sz[v];
}
sz[x]++;
}
void get_st()
{
int i,j;
for(j=;j<=;j++)
for(i=;i<=n;i++)
ff[i][j]=ff[ ff[i][j-] ][j-];
for(i=,lg[]=;i<=cur;i++) lg[i]=lg[i>>]+;
for(j=;j<=lg[cur];j++)
for(i=;i+(<<j)-<=cur;i++)
eu[i][j]=dep[eu[i][j-]] < dep[eu[i+(<<(j-))][j-]] ? eu[i][j-] : eu[i+(<<(j-))][j-];
}
int LCA(int x,int y)
{
x=st[x], y=st[y]; if(x>y) swap(x,y); int l=y-x+;
return dep[eu[x][lg[l]]] < dep[eu[y-(<<lg[l])+][lg[l]]] ? eu[x][lg[l]] : eu[y-(<<lg[l])+][lg[l]];
}
int Dis(int x,int y)
{
if(!x||!y) return inf; int F=LCA(x,y);
return dep[x]+dep[y]-*dep[F];
}
int jump(int x,int D)
{
int i;
for(i=lg[dep[x]-D]+;i>=;i--)
if(ff[x][i] && dep[ff[x][i]]>=D) x=ff[x][i];
return x;
} namespace virtree{ int cmp_dfsorder(int x,int y){ return st[x]<st[y]; }
int vir[N1],num,stk[N1],tp,ctrl[N1],spe[N1],ans[N1],org[N1],dctrl[N1]; void push(int x)
{
int y=stk[tp], F=LCA(x,y); stk[]=F;
while(tp> && dep[stk[tp-]]>dep[F])
{
g.ae(stk[tp-],stk[tp]);
stk[tp]=; tp--;
}
if(dep[stk[tp]]>dep[F])
{
g.ae(F,stk[tp]);
stk[tp]=; tp--;
}
if(!tp||stk[tp]!=F) stk[++tp]=F;
if(stk[tp]!=x) stk[++tp]=x;
}
int build()
{
int i;
for(i=;i<=num;i++) spe[vir[i]]=, org[i]=vir[i];
if(!spe[]) vir[++num]=;
sort(vir+,vir+num+,cmp_dfsorder);
stk[++tp]=vir[];
for(i=;i<=num;i++) push(vir[i]);
while(tp>) g.ae(stk[tp-],stk[tp]), tp--;
return stk[tp];
} int dfs_son(int x)
{
int j,v,mi=;
for(j=g.head[x];j;j=g.nxt[j])
{
v=g.to[j]; dfs_son(v);
mi=chkmin(ctrl[v],mi,dep[ctrl[v]],dep[mi]);
}
ctrl[x]=(spe[x]?x:mi);
return ctrl[x];
}
void dfs_fa(int x)
{
int j,v;
for(j=g.head[x];j;j=g.nxt[j])
{
v=g.to[j];
ctrl[v]=chkmin(ctrl[x],ctrl[v],Dis(ctrl[x],v),Dis(ctrl[v],v));
dfs_fa(v);
}
}
void debug(int x)
{
int j,v;
if(spe[x]) printf("%d ",x);
for(j=e.head[x];j;j=e.nxt[j])
{
v=e.to[j]; if(v==ff[x][]) continue;
debug(v);
}
}
void dfs_ans(int x)
{
int j,v,sum=sz[x],cx=ctrl[x]; dctrl[x]=Dis(x,ctrl[x]);
for(j=g.head[x];j;j=g.nxt[j])
{
v=g.to[j]; dfs_ans(v);
int cv=ctrl[v],p,pv,tmp;
pv=jump(v,dep[x]+);
sum-=sz[pv];
if(dep[v]==dep[x]+) continue;
if(cx!=cv){
tmp=dctrl[x]+dctrl[v]+dep[v]-dep[x];
if(tmp&){
p=dep[v]-((tmp-)/-dctrl[v]);
p=jump(v, min(dep[v],max(dep[x]+,p)) );
}else{
p=dep[v]-((tmp-)/-dctrl[v]);
if(p<=dep[v]){
p=jump(v, min(dep[v],max(dep[x]+,p)) );
if(cv<cx && dep[p]->=dep[x]+) p=ff[p][];
}else p=v;
}
ans[cx]+=sz[pv]-sz[p], ans[cv]+=sz[p]-sz[v];
}else{
ans[cx]+=sz[pv]-sz[v];
}
}
ans[cx]+=sum;
}
void dfs_clear(int x)
{
int j,v;
for(j=g.head[x];j;j=g.nxt[j])
{
v=g.to[j]; dfs_clear(v);
}
g.head[x]=ctrl[x]=spe[x]=ans[x]=;
} void solve(int Num)
{
num=Num;
int root=build(),i;
dfs_son(root);
dfs_fa(root);
dfs_ans(root);
for(i=;i<=Num;i++) if(i!=Num) printf("%d ",ans[org[i]]); else printf("%d\n",ans[org[i]]);
dfs_clear(root);
memset(vir,,(num+)*); memset(org,,(num+)*); memset(stk,,(tp+)*); g.clr(); tp=;
} }; int main()
{
int i,j,ans=,x,y,q,Q;
scanf("%d",&n);
for(i=;i<n;i++) read(x), read(y), e.ae(x,y), e.ae(y,x);
dfs(); get_st(); dep[]=inf;
scanf("%d",&Q);
for(q=;q<=Q;q++)
{
read(x);
for(i=;i<=x;i++) read(virtree::vir[i]);
virtree::solve(x);
}
return ;
}
BZOJ 3572 [HNOI2014]世界树 (虚树+DP)的更多相关文章
- BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]
传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...
- bzoj 3572: [Hnoi2014]世界树 虚树 && AC500
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 520 Solved: 300[Submit][Status] ...
- BZOJ 3572: [Hnoi2014]世界树 虚树 树形dp
https://www.lydsy.com/JudgeOnline/problem.php?id=3572 http://hzwer.com/6804.html 写的时候参考了hzwer的代码,不会写 ...
- bzoj 3572 [Hnoi2014]世界树——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...
- bzoj 3572: [Hnoi2014]世界树 虚树
题目: Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生 ...
- 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增
[题意]给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点.n,m,Σki<=300000. [算法]虚树+倍增 [题解]★参考:thy_asd ...
- bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增
[Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1921 Solved: 1019[Submit][Status][Dis ...
- 洛谷 P3233 [HNOI2014]世界树(虚树+dp)
题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...
- BZOJ 3572: [Hnoi2014]世界树
BZOJ 3572: [Hnoi2014]世界树 标签(空格分隔): OI-BZOJ OI-虚数 OI-树形dp OI-倍增 Time Limit: 20 Sec Memory Limit: 512 ...
随机推荐
- 【cocos2dx 小技巧】半透明屏蔽罩和弹出框的实现
今天介绍一下,弹出框的和屏蔽罩的小实现~ 弹出框主要用到了cocos2dx生命周期里面的OnEnter()函数,就是当Layer被addChild的时候会调用的函数(所以假设把OnEnter的代码加到 ...
- Android与IOS异同点对照(1)------ 显示
Android与IOS作为当前世界上最流行的两大移动端操作系统,都拥有无数的粉丝. 那么对于Android或者IOS的开发人员来说,这两个系统都拥有什么同样或者不同的地方那?如今让我们一起来了解一下A ...
- 我所不知的 javascript Object 的一些现象
1.我们都知道对象访问属性有两种方法,一种是使用 . 操作符,使用它添加的属性名必须满足标识符的命名规范:另一种是使用 [] 键访问的方法,可以接受任意UTF-8/Unicode字符串作为属性名,在E ...
- Ubuntu 16.04 安装CodeBlocks
首先将软件源添加进来,就是运行以下命令 sudo add-apt-repository ppa:damien-moore/codeblocks-stable sudo apt-get update 完 ...
- tab不显示为空格
- B2242 [SDOI2011]计算器
这个题就是把三个数论基础合在了一起,算是一道比较全面的题. 1的时候就是快速幂 2的时候是exgcd求逆元,特殊的,只有两数互质才有逆元. 3就是bsgs啦,还是不太熟 题干: Description ...
- JSP-Runoob:JSP 自动刷新
ylbtech-JSP-Runoob:JSP 自动刷新 1.返回顶部 1. JSP 自动刷新 想象一下,如果要直播比赛的比分,或股票市场的实时状态,或当前的外汇配给,该怎么实现呢?显然,要实现这种实时 ...
- Kubernetes 集群中使用 Helm 搭建 Spinnaker
在我们部署Spinnaker之前,我们需要一个YAML格式的配置文件,它会包含了一些配置信息.可以从Spinnaker Helm Chart repository[2]获得这个文件. $curl -L ...
- eclipse.ini启动参数配置的解析及方法
原文地址 - http://www.uzzf.com/news/18444.html 1.先了解下JVM内存管理机制,JVM内存分为堆内存和非堆内存 2.JVM内存限制 首先JVM内存限制于实际的最大 ...
- Android.mk添加第三方jar包(转载)
转自:www.cnblogs.com/hopetribe/archive/2012/04/23/2467060.html LOCAL_PATH:= $(call my-dir)include $(CL ...