bzoj 3572世界树 虚树+dp
题目大意:
给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制
求每个关键点控制多少个点
分析:
虚树+dp
dp过程如下:
第一次dp,递归求出每个点子树中关键点到它距离最小值
第二次dp,用第一次的信息,从上往下转移,求出每个点到所有关键点中到它距离最小值
这里兼容性讨论一下,发现可以不用存次大值,因为若最小值来自要更新的子树,则子树中点到上面的点的距离一定不优
前两次dp求出了虚树中1,2类点被谁控制
第三次dp,对于每条边,找到断点,细节见代码
注意:
虚树中这样算会算漏很多原树中的点
如2-1-3树,根是1,关键点1,2,这样3会算漏
用tmp存1的sz,遍历虚树子树时把tmp减掉已经算过的,剩下的就属于1的控制
吐槽题目有毒,去行末空格就PE,%……&%……&@¥%&
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
typedef long long LL;
const int M=300007;
const int INF=1e9;
inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=0;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return x;
}
int n,m;
int g[M],hd[M],te,td;
struct edge{int y,next;}e[M<<1],dw[M];
int top[M],dfn[M],pid[M],tdfn;
int pre[M],dep[M],sz[M],son[M];
struct node{
int x,id;
node(int xx=0,int ii=0){x=xx;id=ii;}
}que[M];
bool operator <(node x,node y){
if(x.x<y.x) return 1;
if(x.x==y.x&&x.id<y.id) return 1;
return 0;
}
int st[M],tot;
int ans[M];
int bl[M];
int bid[M];
int pbid[M];
node mn[M];//不求次大值,兼容性的问题
node gmn(node x,node y){
return x<y?x:y;
}
bool cmp(node x,node y){return dfn[x.x]<dfn[y.x];}
void addedge(int x,int y){
e[++te].y=y;e[te].next=g[x];g[x]=te;
}
void addlink(int x,int y){
if(x==y) return;
dw[++td].y=y;dw[td].next=hd[x];hd[x]=td;
}
void dfs1(int x){
sz[x]=1;
int p,y;
for(p=g[x];p;p=e[p].next)
if((y=e[p].y)!=pre[x]){
pre[y]=x;
dep[y]=dep[x]+1;
dfs1(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
void dfs2(int x){
pid[dfn[x]=++tdfn]=x;
int p,y;
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x]);
}
for(p=g[x];p;p=e[p].next)
if((y=e[p].y)!=pre[x]&&y!=son[x]){
top[y]=y;
dfs2(y);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=pre[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
int jumpc(int x,int y){
while(dep[top[x]]>dep[y]){
x=top[x];
if(pre[x]==y) return x;
x=pre[x];
}
return pid[dfn[y]+1];
}
int stp(int x,int y){
int tp;
while( y && (tp=dep[x]-dep[pre[top[x]]])<=y){//y&&
y-=tp;
x=pre[top[x]];
}
return pid[dfn[x]-y];
}
void vbuild(int z){
sort(que+1,que+z+1,cmp);
int i,x,anc;
for(i=1;i<z;i++){
x=LCA(que[i].x,que[i+1].x);
hd[x]=0;
bl[x]=0;
}
hd[1]=0;
bl[1]=0;
for(i=1;i<=z;i++){
x=que[i].x;
hd[x]=0;
bl[x]=que[i].id;
ans[que[i].id]=0;
}
td=tot=0;
st[++tot]=1;
for(i=1;i<=z;i++){
x=que[i].x;
anc=LCA(x,st[tot]);
if(anc==st[tot]) st[++tot]=x;
else{
while(tot>1 && dep[anc]<=dep[st[tot-1]]){
addlink(st[tot-1],st[tot]);
tot--;
}
addlink(anc,st[tot]);
st[tot]=anc;
st[++tot]=x;
}
}
for(i=1;i<tot;i++) addlink(st[i],st[i+1]);
}
void dp1(int x){
if(!bl[x])mn[x]=node(INF,0);
else mn[x]=node(0,pbid[bl[x]]);
int p,y;
node tp;
for(p=hd[x];p;p=dw[p].next){
y=dw[p].y;
dp1(y);
tp=mn[y];
tp.x+=dep[y]-dep[x];
mn[x]=min(mn[x],tp);
}
}
void dp2(int x){
if(!bl[x]) bl[x]=bid[mn[x].id];
int p,y;
node tp;
for(p=hd[x];p;p=dw[p].next){
y=dw[p].y;
tp=mn[x];
tp.x+=dep[y]-dep[x];
mn[y]=min(mn[y],tp);
dp2(y);
}
}
void dp3(int x){
int p,y,z,d,tp;
int ss=sz[x];
for(p=hd[x];p;p=dw[p].next){
y=dw[p].y;
d=mn[x].x+mn[y].x+dep[y]-dep[x]-1;//加上mn,最后要减1,算出要真正要竞争领地的两点间点数,点数!
z=jumpc(y,x);
ss-=sz[z];
dp3(y);
if(bl[x]==bl[y]) ans[bl[x]]+=sz[z]-sz[y];
else{
if(d%2==0){
tp=stp(y,d/2-mn[y].x);
}
else{
if(pbid[bl[y]]<pbid[bl[x]]) tp=stp(y,d/2+1-mn[y].x);//
else tp=stp(y,d/2-mn[y].x);
}
ans[bl[x]]+=sz[z]-sz[tp];
ans[bl[y]]+=sz[tp]-sz[y];
}
}
ans[bl[x]]+=ss;//算漏的补上
}
int main(){
freopen("a.txt","r",stdin);
int i,x,y,z;
n=rd();
for(i=1;i<n;i++){
x=rd(),y=rd();
addedge(x,y);
addedge(y,x);
}
dep[1]=pre[1]=0;
dfs1(1);
top[1]=1;
dfs2(1);
m=rd();
while(m--){
z=rd();
for(i=1;i<=z;i++){
que[i].x=rd();
bid[que[i].x]=i;
que[i].id=i;
pbid[i]=que[i].x;
}
vbuild(z);
dp1(1);
dp2(1);
dp3(1);
for(i=1;i<=z;i++) printf("%d ",ans[i]);
puts("");
}
return 0;
}
bzoj 3572世界树 虚树+dp的更多相关文章
- BZOJ 3572 世界树(虚树)
http://www.lydsy.com/JudgeOnline/problem.php?id=3572 思路:建立虚树,然后可以发现,每条边不是同归属于一端,那就是切开,一半给上面,一半给下面. # ...
- BZOJ 3572 [HNOI2014]世界树 (虚树+DP)
题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...
- BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]
传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...
- 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处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...
- [HNOI2014][bzoj3572] 世界树 [虚树+dp]
题面: 传送门 思路: 一道虚树的好题,是很多虚树博客的入门题目 但是我认为这道题目出的难点和亮点不在于虚树,而在于建出虚树以后dp的思路与实现 下文中为方便描述,用势力范围来表示一个“议事处”管辖的 ...
- bzoj 3572 [Hnoi2014]世界树——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...
- 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增
[题意]给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点.n,m,Σki<=300000. [算法]虚树+倍增 [题解]★参考:thy_asd ...
- bzoj 2286 [Sdoi2011]消耗战 虚树+dp
题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...
随机推荐
- jquery 延迟执行实例介绍
代码如下: $(function(){ var $inputs = $('input[type=button]') .delay(500) .queue(function(){$(this).hide ...
- Eclipse中Ctrl+Alt+Down和Ctrl+Alt+Up不起作用
不起作用是因为跟因特尔的快捷键冲突. 1.在桌面上右键,选择“图形属性......” 2.选择“选项和支持” 3.更改快捷键. 注意:单纯禁用英特尔的快捷键可能不起作用.
- 用telnet命令,POP3接收邮件
昨天已经成功利用telnet命令发送了邮件,今天接着来,只能发送不能接收多郁闷. 邮件的接收这里是基于pop3协议的,pop3协议共定义了12条与接收相关的邮件,如下面简单解释: 首先是与登陆验证相关 ...
- 数的计数(number)
数的计数(number) 题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数n),先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理: (1)不作任何处理: (2)在它的 ...
- angularJS在创建指令需要注意的问题(指令中使用ngRepeat)
现在发现,当初的自己真的是太菜了,为什么你在指令中更改数据,没有作用呢?这其实是原型链的问题. 详细的我就不在这里说了,有位大神早已发布了这个内容,在这里复制个地址给大家,有兴趣的可以看看 http: ...
- IE6下最小19px像素
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- HDU 1434 幸福列车(优先队列)
优先队列的应用 #include<iostream> #include<cstdio> #include<cstring> #include<queue> ...
- Mac os 10.11 更新ruby
1.装cocoapods,ruby版本忒低->开始更新ruby->开始更新gem,这是一条不归路啊同志们,各种permission denied,各种路径不存在,各种路径没有读写权限,各种 ...
- USACO Section 1.3 Barn Repair 解题报告
题目 题目描述 某农夫有一个养牛场,所有的牛圈都相邻的排成一排(共有S个牛圈),每个牛圈里面最多只圈养一头牛.有一天狂风卷积着乌云,电闪雷鸣,把牛圈的门给刮走了.幸运的是,有些牛因为放假,所以没在自己 ...
- pthread_join
摘要:pthread_join使一个线程等待另一个线程束. 代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了.加入pthread_jo ...