码迷,mamicode.com
首页 > 其他好文 > 详细

[bzoj3572] [Hnoi2014]世界树

时间:2016-02-13 18:15:30      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:

  十分感人的一道题。。虚树+倍增。。

  想了半天发现我竟然会写?而且似乎想对了。。。。然而细节打挂调了一个下午>_<

  首先把虚树建出来,然后在虚树上跑两遍dfs求出每个点最接近的议事处与距离,再然后计算虚树上每一条边对答案的贡献。。

  令mndis[x]表示点x离最近议事处的距离,mnpos[x]表示点x最近的议事处是哪个。

  对于虚树上每条边i->j(i 是父亲),

  计算贡献时要分两种情况:

  1、mnpos[i]==mnpos[ j ]:这时候,原树上i 到 j 的那条链(不包括i 和j )及其孩子都归mnpos[i]管辖了。

    我们可以先求出i 往 j 那个方向的第一个孩子记为next,

    那么i-> j 这条边对mnpos[i]的贡献就是size[next]-next[ j ]。(size[x]表示原树里x子树里(包括x)点的个数)

  2、mnpos[i]不等于mnpos[ j ]的时候,显然mnpos[i]不在 j 的子树里,而mnpos[ j ]在 j 的子树里。

    现在i-> j 这条边会被分成两部分。。。我们只要找到分界点mid的位置就行了。。

    两个议事处的距离dis等于mndis[i]+mndis[j]+( 边i-> j 的长度 ),

    然后我们就知道mid是mnpos[ j ]往上数多少个祖先。这里似乎只能用倍增求mid= =

    之后的计算就类似情况1了。

 

  注意:到这时我们只计算了边的贡献,记得最后把没被考虑到的点要加上去;另外这题行末要有空格...吃了一发PE

技术分享
  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn=300023;
  7 const int inf=1000233333;
  8 struct zs{
  9     int too,pre;
 10 }e[maxn<<1];
 11 struct zs1{
 12     int too,pre,dis;
 13 }e1[maxn];
 14 int tot,tot1,last[maxn],last1[maxn];
 15 int size[maxn],dfn[maxn],bel[maxn],fa[maxn],sum[maxn],dep[maxn],next[maxn],tim;
 16 int mndis[maxn],mnpos[maxn],id[maxn],sz[maxn];
 17 int st[maxn],top,poi[maxn],tmp[maxn];
 18 int FA[maxn][20],ans[maxn];
 19 int i,j,k,K,n,m,a;
 20  
 21 int ra;char rx;
 22 inline int read(){
 23     rx=getchar(),ra=0;
 24     while(rx<0||rx>9)rx=getchar();
 25     while(rx>=0&&rx<=9)ra*=10,ra+=rx-48,rx=getchar();return ra;
 26 }
 27  
 28  
 29 inline void insert(int a,int b){
 30     e[++tot].too=b,e[tot].pre=last[a],last[a]=tot;
 31     e[++tot].too=a,e[tot].pre=last[b],last[b]=tot;
 32 }
 33 inline void ins(int a,int b){
 34 //  printf("   %d-->%d\n",a,b);
 35     e1[++tot1].too=b,e1[tot1].dis=dep[b]-dep[a],
 36     e1[tot1].pre=last1[a],last1[a]=tot1;
 37 }
 38  
 39 void dfs(int x){
 40     dep[x]=dep[fa[x]]+1,size[x]=1;
 41     for(int i=1;i<=20&&(1<<i)<dep[x];i++)
 42         FA[x][i]=FA[FA[x][i-1]][i-1];
 43     for(int i=last[x];i;i=e[i].pre)if(e[i].too!=fa[x])
 44         fa[e[i].too]=FA[e[i].too][0]=x,dfs(e[i].too),size[x]+=size[e[i].too];
 45 }
 46 void dfs2(int x,int chain){
 47     int i,mxpos=0;
 48     bel[x]=chain,dfn[x]=++tim;
 49     if(dep[x]>2)sum[x]=sum[fa[x]]+size[fa[x]]-size[x];
 50     for(i=last[x];i;i=e[i].pre)
 51         if(e[i].too!=fa[x]&&size[e[i].too]>=size[mxpos])mxpos=e[i].too;
 52     if(!mxpos)return;
 53     dfs2(mxpos,chain);next[x]=mxpos;
 54     for(i=last[x];i;i=e[i].pre)
 55         if(e[i].too!=fa[x]&&e[i].too!=mxpos)dfs2(e[i].too,e[i].too);
 56 }
 57 inline int getlca(int a,int b){
 58     if(dep[bel[a]]<dep[bel[b]])swap(a,b);
 59     while(bel[a]!=bel[b]){
 60         a=fa[bel[a]];
 61         if(dep[bel[a]]<dep[bel[b]])swap(a,b);
 62     }
 63     return dep[a]<dep[b]?a:b;
 64 }
 65 inline int getnext(int f,int j){//找到f往j方向的第一个孩子 
 66     int pre=0;
 67     while(bel[j]!=bel[f])pre=bel[j],j=fa[bel[j]];
 68     return j==f?pre:next[f];
 69 }
 70 inline int getancestor(int x,int d){//找到x往上d 代祖先 
 71     for(register int i=19;i>=0;i--)
 72         if((1<<i)&d)x=FA[x][i];
 73     return x;
 74 }
 75  
 76 void build(){
 77     int i,lca;
 78     st[top=1]=poi[1];
 79     for(i=2;i<=K;i++){
 80         lca=getlca(poi[i],st[top]);
 81         while(dfn[st[top]]>dfn[lca])
 82             if(dfn[st[--top]]<=dfn[lca]){
 83                 ins(lca,st[top+1]);
 84                 if(lca!=st[top])st[++top]=lca;
 85             }else ins(st[top],st[top+1]);
 86         st[++top]=poi[i];
 87     }
 88     while(top>1)top--,ins(st[top],st[top+1]);
 89 }
 90 void run1(int x){
 91     int tmpmn=id[x]==m?0:inf,tmppos=tmpmn==0?x:inf;
 92     sz[x]=id[x]==m;
 93     for(int i=last1[x];i;i=e1[i].pre){
 94         int to=e1[i].too;
 95         run1(to);sz[x]+=sz[to];
 96         if( (mndis[to]+e1[i].dis<tmpmn)
 97             ||(mndis[to]+e1[i].dis==tmpmn&&mnpos[to]<tmppos) )
 98             tmpmn=mndis[to]+e1[i].dis,tmppos=mnpos[to];
 99     }
100     mnpos[x]=tmppos,mndis[x]=tmpmn;
101 }
102 void run2(int x){
103     ans[x]=0;
104     for(int i=last1[x],to=e1[i].too;i;to=e1[i=e1[i].pre].too){
105         if( (e1[i].dis+mndis[x]<mndis[to])
106           ||(e1[i].dis+mndis[x]==mndis[to]&&mnpos[x]<mnpos[to]) )
107           mndis[to]=e1[i].dis+mndis[x],mnpos[to]=mnpos[x];
108         run2(to);
109     }
110 }
111 void run3(int x){
112     if(sz[x]==1){ans[x]+=size[x];last1[x]=0;return;}
113     int i,to,mid,next,j,k=mnpos[x],tmpsize=size[x];
114      
115     for(to=e1[i=last1[x]].too;i;to=e1[i=e1[i].pre].too){
116         run3(to);
117         next=getnext(x,to),j=mnpos[to],tmpsize-=size[next];
118         if(j==k)ans[j]+=size[next]-size[to];
119         else
120             mid=(mndis[x]+e1[i].dis+mndis[to]),
121             mid=getancestor(j,(mid>>1)-(!(mid&1)&&k<j)),
122             ans[j]+=size[mid]-size[to],ans[k]+=size[next]-size[mid];
123     }
124     ans[k]+=tmpsize,last1[x]=0;
125 }
126  
127 bool cmp(int a,int b){return dfn[a]<dfn[b];}
128 int main(){
129     n=read();
130     for(i=1;i<n;i++)a=read(),insert(a,read());
131     dfs(1);dfs2(1,1);
132     for(m=read();m;m--){
133         K=read();tot1=0;
134         for(i=1;i<=K;i++)id[poi[i]=tmp[i]=read()]=m;
135         sort(poi+1,poi+1+K,cmp),
136         build(),run1(st[1]),run2(st[1]);
137         run3(st[1]);ans[mnpos[st[1]]]+=n-size[st[1]];
138         for(i=1;i<=K;i++)printf("%d ",ans[tmp[i]]);puts("");
139         if(m>1)for(i=1;i<=K;i++)ans[tmp[i]]=0;
140     }
141      
142     return 0;
143 }
View Code

感人的是我把计算时的东西一次就考虑全了。。。结果多组数据..初始化写残吃了若干发RE..T_T,一开始没发现还写了非递归(反正都把dfn给搞出来了)>_<

中间手贱还CE了(掀桌。。

幸好我能用链剖的地方都用了链剖。。。刚好卡在#10.....

[bzoj3572] [Hnoi2014]世界树

标签:

原文地址:http://www.cnblogs.com/czllgzmzl/p/5188065.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!