bzoj 1316: 树上的询问 (点分治)

来源:互联网 发布:王千源事件 知乎 编辑:程序博客网 时间:2024/05/18 11:07

1316: 树上的询问

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 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

Sample Output

Yes
Yes
No
Yes


HINT

30%的数据,n≤100. 
100%的数据,n≤10000,p≤100,长度≤1000000. 

做完此题可看下POJ 3237 Tree

Source

[Submit][Status][Discuss]

题解:点分治

#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");  }  


0 0
原创粉丝点击