posts - 43,  comments - 9,  trackbacks - 0

题目给出一棵树(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>
  3using namespace std;
  4
  5//#define DEBUG
  6
  7const int MAX_V = 11000;
  8const int MAX_E = MAX_V*5;
  9
 10struct EDGE{
 11  int v,e,w;
 12}
edg[MAX_E];
 13int se, gg[MAX_V];
 14bool ok[MAX_E];
 15int vis[MAX_V], stamp;
 16int que[MAX_V*5], sque, pque;
 17int card[MAX_V], chm[MAX_V], sum[MAX_V], det[MAX_V];
 18int gdd[MAX_V],sdd;
 19int ans;
 20int N,L; 
 21
 22void 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
 28void 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
 47int 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
 73void 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
111int 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
posted on 2009-07-31 14:30 wolf5x 阅读(493) 评论(0)  编辑 收藏 引用 所属分类: acm_icpc

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

"Do not spend all your time on training or studying - this way you will probably become very exhausted and unwilling to compete more. Whatever you do - have fun. Once you find programming is no fun anymore – drop it. Play soccer, find a girlfriend, study something not related to programming, just live a life - programming contests are only programming contests, and nothing more. Don't let them become your life - for your life is much more interesting and colorful." -- Petr

留言簿(3)

随笔分类(59)

随笔档案(43)

cows

搜索

  •  

最新评论

评论排行榜