1. 程式人生 > >[BZOJ1316]樹上的詢問 點分治

[BZOJ1316]樹上的詢問 點分治

lin line find bzoj mem while 如果 輸出 php

1316: 樹上的詢問

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1017 Solved: 287
[Submit][Status][Discuss]

Description

一棵n個點的帶權有根樹,有p個詢問,每次詢問樹中是否存在一條長度為Len的路徑,如果是,輸出Yes否輸出No.

Input

第一行兩個整數n, p分別表示點的個數和詢問的個數. 接下來n-1行每行三個數x, y, c,表示有一條樹邊x→y,長度為c. 接下來p行每行一個數Len,表示詢問樹中是否存在一條長度為Len的路徑.

Output

輸出有p行,Yes或No.

Sample Input

6 4
1 2 5
1 3 7
1 4 1
3 5 2
3 6 3
1
8
13
14

Sample Output

Yes
Yes
No
Yes


HINT

30%的數據,n≤100.
100%的數據,n≤10000,p≤100,長度≤1000000.

做完此題可看下POJ 3237 Tree

Source

技術分享圖片
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<algorithm>
 6
#include<cmath> 7 #define maxn 100000 8 using namespace std; 9 int n,q; 10 int ask[maxn]; 11 int ans[maxn]; 12 struct edge { 13 int to,next,c; 14 }e[maxn*2]; 15 int head[maxn],cnt; 16 void add(int u,int v,int c){e[cnt].to=v;e[cnt].next=head[u];e[cnt].c=c;head[u]=cnt++;} 17 int root; 18 int vis[maxn],size[maxn],sum,f[maxn];
19 void findrt(int x,int fa) { 20 size[x]=1;f[x]=0; 21 for(int i=head[x];i>=0;i=e[i].next) { 22 int to=e[i].to;if(to==fa||vis[to]) continue; 23 findrt(to,x); 24 size[x]+=size[to]; 25 f[x]=max(f[x],size[to]); 26 } 27 f[x]=max(f[x],sum-size[x]); 28 if(f[x]<f[root]) root=x; 29 } 30 int dis[maxn],t[maxn],tt,tmp,num[maxn]; 31 int dfs(int x,int fa,int d) { 32 dis[++tt]=d; 33 for(int i=head[x];i>=0;i=e[i].next) { 34 int to=e[i].to;if(to==fa||vis[to]) continue; 35 dfs(to,x,d+e[i].c); 36 } 37 } 38 void cal(int x,int fl,int d) { 39 tt=0;tmp=0; 40 dfs(x,0,d); 41 sort(dis+1,dis+tt+1); 42 for(int i=1;i<=tt;i++) { 43 if(dis[i]!=dis[i-1]||i==1) dis[++tmp]=dis[i],num[tmp]=1; 44 else num[tmp]++; 45 } 46 for(int i=1;i<=q;i++) { 47 for(int j=1;j<=tmp;j++) 48 if(num[j]>=2&&dis[j]*2==ask[i]) ans[i]+=fl*num[j]*(num[j]-1); 49 int l=1,r=tmp; 50 while(l<r) { 51 if(dis[l]+dis[r]>ask[i]&&l<r) r--; 52 else { 53 if(dis[l]+dis[r]==ask[i]) ans[i]+=fl*num[l]*num[r]; 54 l++; 55 } 56 } 57 } 58 59 } 60 void work(int x) { 61 vis[x]=1;cal(x,1,0); 62 for(int i=head[x];i>=0;i=e[i].next) { 63 int to=e[i].to;if(vis[to]) continue; 64 cal(to,-1,e[i].c); 65 root=0;sum=size[to];findrt(to,x); 66 work(root); 67 } 68 } 69 int main() { 70 memset(head,-1,sizeof(head)); 71 scanf("%d%d",&n,&q); 72 for(int i=1;i<n;i++) { 73 int a,b,c; 74 scanf("%d%d%d",&a,&b,&c); 75 add(a,b,c);add(b,a,c); 76 } 77 for(int i=1;i<=q;i++) scanf("%d",&ask[i]); 78 f[0]=2147483647;sum=n; 79 findrt(1,0); 80 work(root); 81 for(int i=1;i<=q;i++) 82 if(ans[i]>0||!ask[i]) printf("Yes\n"); 83 else printf("No\n"); 84 }
View Code

[BZOJ1316]樹上的詢問 點分治