1316: 树上的询问
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 564 Solved: 150
[ 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
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
Yes
No
Yes
HINT
30%的数据,n≤100.
100%的数据,n≤10000,p≤100,长度≤1000000.
做完此题可看下POJ 3237 Tree
Source
题解:点分治
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 30003
using namespace std;
int n,m,tot,num[10000003];
int point[N],next[N],v[N],len[N],cnt,root,sum,vis[N];
int f[N],deep[N],mp[N],d[N],ak[N],mark[N],son[N],g[N];
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; len[tot]=z;
//cout<<x<<" "<<y<<" "<<z<<endl;
}
void getroot(int x,int fa)
{
son[x]=1; f[x]=0;
for (int i=point[x];i;i=next[i]){
if (vis[v[i]]||v[i]==fa) continue;
getroot(v[i],x);
son[x]+=son[v[i]];
f[x]=max(f[x],son[v[i]]);
}
f[x]=max(f[x],sum-son[x]);
if (f[x]<f[root]) root=x;
}
void getdeep(int x,int fa)
{
deep[++deep[0]]=d[x];
for (int i=point[x];i;i=next[i]){
if (vis[v[i]]||v[i]==fa) continue;
d[v[i]]=d[x]+len[i];
getdeep(v[i],x);
}
}
void cal(int x,int now,int opt)
{
d[x]=now; deep[0]=0;
getdeep(x,0);
sort(deep+1,deep+deep[0]+1);
g[0]=0;
for (int i=1;i<=deep[0];i++)
if (deep[i]!=deep[i-1]||i==1)
g[++g[0]]=deep[i],num[g[0]]=1;
else num[g[0]]++;
for (int i=1;i<=m;i++) {
int l=1; int r=g[0];
for (int j=1;j<=g[0];j++)
if (num[j]>1&&g[j]*2==ak[i])
mark[i]+=opt*num[j]*(num[j]-1);
while (l<r) {
if (g[l]+g[r]<ak[i]) l++;
else {
if (g[l]+g[r]==ak[i]) mark[i]+=opt*num[l]*num[r];
r--;
}
}
}
}
void work(int x)
{
cal(x,0,1);
vis[x]=1;
for (int i=point[x];i;i=next[i]){
if (vis[v[i]]) continue;
cal(v[i],len[i],-1);
sum=son[v[i]]; root=0;
getroot(v[i],x);
work(root);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++) {
int x,y,z; scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
for (int i=1;i<=m;i++) scanf("%d",&ak[i]);
sum=n; f[0]=1000000000; root=0;
getroot(1,0);
work(root);
for (int i=1;i<=m;i++)
if(mark[i]>0||!ak[i]) printf("Yes\n");
else printf("No\n");
}