BZOJ 3572 世界树(虚树)

http://www.lydsy.com/JudgeOnline/problem.php?id=3572

思路:建立虚树,然后可以发现,每条边不是同归属于一端,那就是切开,一半给上面,一半给下面。

  1 #include<algorithm>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<iostream>
  6 #define N 300005
  7 int tot,go[N*2],next[N*2],first[N],sz,deep[N],tmp[N],tree[N];
  8 int son[N],dfn[N],fa[N][20],bin[20],ask[N],In[N],st[N],n,m,ans[N];
  9 int father[N],val[N];
 10 std::pair<int,int>near[N];
 11 int read(){
 12     int t=0,f=1;char ch=getchar();
 13     while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘) f=-1;ch=getchar();}
 14     while (‘0‘<=ch&&ch<=‘9‘){t=t*10+ch-‘0‘;ch=getchar();}
 15     return t*f;
 16 }
 17 void insert(int x,int y){
 18     tot++;
 19     go[tot]=y;
 20     next[tot]=first[x];
 21     first[x]=tot;
 22 }
 23 void add(int x,int y){
 24     insert(x,y);insert(y,x);
 25 }
 26 void dfs(int x){
 27     son[x]=1;
 28     dfn[x]=++sz;
 29     for (int i=1;i<=19;i++)
 30      fa[x][i]=fa[fa[x][i-1]][i-1];
 31     for (int i=first[x];i;i=next[i]){
 32         int pur=go[i];
 33         if (pur==fa[x][0]) continue;
 34         deep[pur]=deep[x]+1;
 35         fa[pur][0]=x;
 36         dfs(pur);
 37         son[x]+=son[pur];
 38     }
 39 }
 40 int find(int x,int dep){
 41     for (int i=19;i>=0;i--)
 42      if (deep[fa[x][i]]>=dep) x=fa[x][i];
 43     return x;
 44 }
 45 int lca(int x,int y){
 46     if (deep[x]<deep[y]) std::swap(x,y);
 47     int t=deep[x]-deep[y];
 48     for (int i=0;i<=19;i++)
 49      if (t&bin[i])
 50       x=fa[x][i];
 51     if (x==y) return x;
 52     for (int i=19;i>=0;i--)
 53      if (fa[x][i]!=fa[y][i])
 54       x=fa[x][i],y=fa[y][i];
 55     return fa[x][0];
 56 }
 57 bool cmp(int a,int b){
 58     return dfn[a]<dfn[b];
 59 }
 60 void solve(){
 61     m=read();
 62     for (int i=1;i<=m;i++){
 63         ask[i]=read(),tmp[i]=tree[i]=ask[i];
 64         near[ask[i]]=std::make_pair(0,ask[i]);
 65         ans[ask[i]]=0;
 66     }
 67     std::sort(ask+1,ask+1+m,cmp);
 68     int top=0,all=m;
 69     for (int i=1;i<=m;i++){
 70         int p=ask[i];
 71         if (!top) father[p]=0,st[++top]=p;
 72         else{
 73             int x=lca(st[top],p);
 74             father[p]=x;
 75             while (top&&deep[st[top]]>deep[x]){
 76                 if (deep[st[top-1]]<=deep[x]){
 77                     father[st[top]]=x;
 78                 }
 79                 top--;
 80             }
 81             if (st[top]!=x){
 82                 father[x]=st[top];tree[++all]=x;
 83                 st[++top]=x;near[x]=std::make_pair(1<<30,0);
 84             }
 85             st[++top]=p;
 86         }
 87     }
 88     std::sort(tree+1,tree+1+all,cmp);
 89     for (int i=1;i<=all;i++){
 90         int p=tree[i],f=father[p];
 91         val[p]=son[p];
 92         if (i>1) In[p]=deep[p]-deep[f];
 93     }
 94     for (int i=all;i>1;i--){
 95         int p=tree[i],f=father[p];
 96         near[f]=std::min(near[f],std::make_pair(near[p].first+In[p],near[p].second));
 97     }
 98     for (int i=2;i<=all;i++){
 99         int p=tree[i],f=father[p];
100         near[p]=std::min(near[p],std::make_pair(near[f].first+In[p],near[f].second));
101     }
102     for (int i=1;i<=all;i++){
103         int p=tree[i],f=father[p],sum=son[find(p,deep[f]+1)]-son[p];
104         if (f==0) ans[near[p].second]+=n-son[p];
105         else{
106             val[f]-=sum+son[p];
107             if (near[p].second==near[f].second) ans[near[p].second]+=sum;
108             else{
109                 int dis=(deep[p]-deep[f]-near[p].first+near[f].first)/2;
110                 if (dis+near[p].first==near[f].first+deep[p]-deep[f]-dis&&near[f].second<near[p].second) dis--;
111                 int x=find(p,deep[p]-dis);
112                 ans[near[p].second]+=son[x]-son[p];
113                 ans[near[f].second]+=sum+son[p]-son[x];
114             }
115         }
116     }
117     for (int i=1;i<=all;i++){
118         ans[near[tree[i]].second]+=val[tree[i]];
119     }
120     for (int i=1;i<=m;i++)
121      printf("%d ",ans[tmp[i]]);
122     puts("");
123 }
124 int main(){
125     n=read();
126     bin[0]=1;
127     for (int i=1;i<=19;i++) bin[i]=bin[i-1]*2;
128     for (int i=1;i<n;i++){
129         int x=read(),y=read();
130         add(x,y);
131     }
132     dfs(1);
133     int T=read();
134     while (T--) solve();
135 }
时间: 2024-12-15 07:04:38

BZOJ 3572 世界树(虚树)的相关文章

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

传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$(f,x)$,讨论一下边上的点的子树应该靠谁更近 倍增求出分界点 注意有些没出现在虚树上的子树 注意讨论的时候只讨论链上的不包括端点,否则$f$的子树会被贡献多次 学到的一些$trick:$ 1.$pair$的妙用 2.不需要建出虚树只要求虚树的$dfs$序(拓扑序)和$fa$就可以$DP$了 注意

【BZOJ-3572】世界树 虚树 + 树形DP

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种

[HNOI2014][BZOJ3572] 世界树|虚树|树上倍增LCA|树型dp|dfs序

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 555  Solved: 319[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.    世界树的形态可以用一个数学模型来描述:世界树中有n个种

【BZOJ3572】【Hnoi2014】世界树 虚树

链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/46506883"); } 题解: 首先构建虚树,然后在虚树上DP. 过程很简单. 先找出每个虚树节点 i 旁边最近的询问节点 neari (因为有一些lca也被加入了虚树所以虚树节点不全是询问节点,呃怕你们不懂,但其实

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

luogu3233 世界树 (虚树)

反正肯定要建虚树,考虑建完之后怎么做 先随便dp一下算出来距离某点最近的询问点mi[x](因为有的虚树上的点它不是询问点嘛) 那我们对于某条链x到fa[x]上的非虚树上的点(包括他们的非虚树上的孩子),要么把它分给mi[x],要么分给mi[fa[x]] 我找到这个中间点以后,在原树上倍增跳过去,算他的size 这个分法是以$\frac{len[mi[x]]+len[x]+len[mi[fa[x]]]}{2}$再加加减减一些细节决定的(len[x]表示x到fa[x]的链的长度) 除此之外,每个在虚

bzoj 3572 [Hnoi2014]世界树(虚树+DP)

3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 645  Solved: 362[Submit][Status][Discuss] Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石.     世界树的形态可以用一个数学模型来描述:世界树中有n个

BZOJ 3572 【HNOI2014】 世界树

题目链接:世界树 首先看到\(\sum m_i\le 3\times 10^5\)这个条件,显然这道题就需要用虚树了. 在我们构建出虚树之后,就可以用两遍\(dfs\)来求出离每个点最近的议事处了.然后,如果一个点和它在虚树上的父亲所属的议事处不同,那么在原树中的两点之间的路径上就会存在一个分界点,倍增出这个分界点算答案即可.注意不能用路径长度来算,需要使用子树大小. 然后我们还需要考虑那些不在虚树中的点.我们可以令\(f_x\)表示原树上\(x\)子树中和\(x\)属于同一个议事处的点的个数,

数据结构(虚树,动态规划):HNOI 2014 世界树

Hnoi2014 世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. 世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相 同.有的聚居地之间有双向的道路相连,道路的长度为1.保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互