pku 2114 Boatherds (树的分治)

pku 2114 Boatherds (树的分治)

题目给出一棵树(N<=10000)以及所有边的权(<=10000). 现在要求对任意查询Q(<=10^8), 判断是否存在一条边权之和恰好为Q的路径.
标程的方法暂时没看懂= =... 
我用树的分治做(更多相关内容见09国家集训队漆子超的论文)
考虑一棵以root为根的子树, 那么在这棵子树中, 有多少条 v1->root->v2 的路径长恰好为Q呢? 可以先DFS一次子树, 求出所有点到root的距离, 将这些距离排个序,  就能在klogk的时间里求出满足和为Q的组合数count[root,Q]. 但是这样把路径重叠, 也就是v1->v'->root->v'->v2的情况也算进去了.  考虑不合法的状态, 如果有边重复, 那么其中肯定包含root到某个儿子的边, 也就是形如v1->chi->root->chi->v2. 所以count[root,Q]中不合法路径数为sigma(count[chi,Q-2*dist[root][chi]]), 求出来减去即可.
这样处理完以root为根的树后, 结点root已经没有用了. 也就是说剩下的只须处理它的所有儿子树,  转化为新的独立问题.
这样不断地处理树, 将树分成新子树, 最后完成所有子树的统计.

恰当选择每次拆分的树根可以使复杂度变成nlogn, 否则总复杂度仍然是n^2. 一个较方便的规则是: 选择树的平衡点, 也就是拿掉这个点后形成的森林里, max(|tree[i]|)最小.
这样的点可以对树DFS一次做DP求得. pku1655就是解决这个问题.

ps.pku1741与此题几乎完全一样, 唯一不同的是那题是求dist<=Q的路径的数量.

代码:

  1 #include  < iostream >
  2 #include  < algorithm >
  3 using   namespace  std;
  4
  5 // #define DEBUG
  6
  7 const   int  MAX_V  =   11000 ;
  8 const   int  MAX_E  =  MAX_V * 5 ;
  9
 10 struct  EDGE {
 11  int v,e,w;
 12}
edg[MAX_E];
 13 int  se, gg[MAX_V];
 14 bool  ok[MAX_E];
 15 int  vis[MAX_V], stamp;
 16 int  que[MAX_V * 5 ], sque, pque;
 17 int  card[MAX_V], chm[MAX_V], sum[MAX_V], det[MAX_V];
 18 int  gdd[MAX_V],sdd;
 19 int  ans;
 20 int  N,L; 
 21
 22 void  addedge( int  u,  int  v,  int  w)
 23 {
 24  edg[se].v=v; edg[se].w=w; edg[se].e=gg[u]; gg[u]=se++;
 25  edg[se].v=u; edg[se].w=w; edg[se].e=gg[v]; gg[v]=se++;
 26}

 27
 28 void  dfsplus( int  rt,  int  ss,  int  di,  int   & idx,  int   & cnt) 
 29 {
 30  //求距离的同时找重心
 31  vis[rt]=stamp;
 32  chm[rt]=0, card[rt]=1;
 33  gdd[sdd++]=di;
 34  for(int j=gg[rt]; j>0; j=edg[j].e){
 35    if(ok[j])continue;
 36    int v=edg[j].v;
 37    if(vis[v]==stamp)continue;
 38    dfsplus(v, ss, di+edg[j].w, idx, cnt);
 39    card[rt]+=card[v];
 40    chm[rt]=max(chm[rt],card[v]);
 41  }

 42  chm[rt]=max(chm[rt],ss-card[rt]);
 43  if(chm[rt]<cnt)
 44    idx=rt, cnt=chm[rt];
 45}

 46
 47 int  matchdist( int  d)
 48 {
 49  int ret=0;
 50  sort(gdd,gdd+sdd);
 51  int q1=0, q2=sdd-1;
 52  //线性的扫描求组合数, 可惜前面的排序多了个logn
 53  while(q1<=q2){
 54    if(gdd[q1]+gdd[q2]<d)q1++;
 55    else if(gdd[q1]+gdd[q2]>d)q2--;
 56    else{
 57      if(gdd[q1]==gdd[q2]){
 58        ret+=(q2-q1)*(q2-q1+1)/2
 59        break;
 60      }

 61      else{
 62        int p1=q1+1, p2=q2-1;
 63        while(p1<sdd && gdd[p1]==gdd[q1]) p1++;
 64        while(p2>=0 && gdd[p2]==gdd[q2]) p2--;
 65        ret+=(p1-q1)*(q2-p2);
 66        q1=p1, q2=p2;
 67      }

 68    }

 69  }

 70  return ret;
 71}

 72
 73 void  solve()
 74 {
 75  int i,j,k;  
 76  pque=sque=0;
 77  ans=0;
 78  stamp=0;
 79  memset(vis,0,sizeof(vis));
 80  det[1]=-1; sum[1]=N;
 81  que[sque++]=1;
 82  while(pque!=sque){
 83    int u=que[pque++];
 84    int idx=u, cnt=N+1;
 85    sdd=0;
 86    ++stamp;
 87    dfsplus(u, sum[u], 0, idx, cnt);
 88    if(det[u]>0 && L-det[u]*2>=0){
 89      //减去它父亲的非法路径数
 90      ans-=matchdist(L-det[u]*2);
 91    }

 92    u=idx;
 93    sdd=0;
 94    ++stamp;
 95    dfsplus(u, sum[u], 0, idx, cnt);
 96    //加上自己的总路径数
 97    ans+=matchdist(L);
 98    for(j=gg[u]; j>0; j=edg[j].e){
 99      if(ok[j])continue;
100      int v=edg[j].v;
101      det[v]=edg[j].w;
102      ok[j^1]=true//切断子树和父亲的边
103      sum[v]=card[v];
104      que[sque++]=v;
105    }

106  }

107  if(ans>0) puts("AYE");
108  else puts("NAY");
109}

110
111 int  main()
112 {
113  int i,j,k,u,v,w;
114  while(true){
115    scanf("%d",&N);
116    if(N==0)break;
117    se=2;
118    memset(gg,0,sizeof(gg));
119    for(u=1; u<=N; u++){
120      while(true){
121        scanf("%d",&v);
122        if(v==0)break;
123        scanf("%d",&w);
124        addedge(u,v,w);
125      }

126    }

127    while(true){
128      scanf("%d",&L);
129      if(L==0)break;
130      memset(ok,false,sizeof(ok));
131      solve();
132    }

133    printf(".\n");
134  }

135}

136

你可能感兴趣的:(pku 2114 Boatherds (树的分治))