题目给出一棵树(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