【点分治】bzoj1316 树上的询问

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 10001
#define INF 2147483647
typedef pair<int,int> Point;
int n,m,Ks[101];
bool vis[101];
int v[MAXN<<1],w[MAXN<<1],first[MAXN],next[MAXN<<1],en;
int dis[MAXN],En,last;
void AddEdge(const int &U,const int &V,const int &W)
{
    v[++en]=V;
    w[en]=W;
    next[en]=first[U];
    first[U]=en;
}
bool centroid[MAXN];
int size[MAXN];
int calc_sizes(int U,int Fa)
{
    int res=1;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        res+=calc_sizes(v[i],U);
    return size[U]=res;
}
Point calc_centroid(int U,int Fa,int nn)
{
    Point res=make_pair(INF,-1);
    int sum=1,maxv=0;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        {
          res=min(res,calc_centroid(v[i],U,nn));
          maxv=max(maxv,size[v[i]]);
          sum+=size[v[i]];
        }
    maxv=max(maxv,nn-sum);
    res=min(res,make_pair(maxv,U));
    return res;
}
void calc_dis(int U,int Fa,int d)
{
    dis[En++]=d;
    for(int i=first[U];i;i=next[i])
      if(v[i]!=Fa&&(!centroid[v[i]]))
        calc_dis(v[i],U,d+w[i]);
}
void calc_pairs()
{
    sort(dis,dis+last);
    for(int i=last;i<En;++i)
      for(int j=1;j<=m;++j)
        {
          int *p=lower_bound(dis,dis+last,Ks[j]-dis[i]);
          if(*p==Ks[j]-dis[i]&&p!=dis+last)
            vis[j]=1;
        }
}
void solve(int U)
{
    calc_sizes(U,-1);
    int s=calc_centroid(U,-1,size[U]).second;
    centroid[s]=1;
    for(int i=first[s];i;i=next[i])
      if(!centroid[v[i]])
        solve(v[i]);
    En=0; dis[En++]=0;
    for(int i=first[s];i;i=next[i])
      if(!centroid[v[i]])
        {
          last=En;
          calc_dis(v[i],s,w[i]);
          calc_pairs();
        }
    centroid[s]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    int a,b,c;
    for(int i=1;i<n;++i)
      {
        scanf("%d%d%d",&a,&b,&c);
        AddEdge(a,b,c); AddEdge(b,a,c);
      }
    for(int i=1;i<=m;++i) scanf("%d",&Ks[i]);
    solve(1);
    for(int i=1;i<=m;++i) puts((Ks[i]==0||vis[i])?"Yes":"No");
    return 0;
}
时间: 2024-10-10 19:25:35

【点分治】bzoj1316 树上的询问的相关文章

[bzoj1316]树上的询问_点分治

树上的询问 bzoj-1316 题目大意:一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 注释:$1\le n\le 10^4$,$1\le p\le 100$,长度$\le 10^6$. 想法:有根树tm是啥意思?根在jb哪呢?老子我瞅tm这么半天也没看见根在哪呢??这题点分治即可.我们用点分治的第二种:分别计算子树,然后用之前的信息更新答案.对于此题,我们可以直接维护一个set就行. 最后,附上丑陋的代码... ... #inc

[bzoj1316] 树上的询问

裸的点分治.. 及时把已经确定的询问清掉就能快不少.时间复杂度O(nlogn*p) 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=10023; 7 struct zs{int len,id;bool done;}q[103]; 8 struct zs1{int t

【BZOJ1316】树上的询问 点分治+set

[BZOJ1316]树上的询问 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

【bzoj1316】树上的询问 树的点分治+STL-set

题目描述 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. 输入 第一行两个整数n, p分别表示点的个数和询问的个数. 接下来n-1行每行三个数x, y, c,表示有一条树边x→y,长度为c. 接下来p行每行一个数Len,表示询问树中是否存在一条长度为Len的路径. 输出 输出有p行,Yes或No. 样例输入 6 4 1 2 5 1 3 7 1 4 1 3 5 2 3 6 3 1 8 13 14 样例输出 Yes Yes No Yes

BZOJ 1316: 树上的询问 (点分治+set)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1316 因为只要求存在某条路径长度为K,所以点分,然后用set判断差值是否在set中就可以了. #include<cstring>#include<algorithm>#include<iostream>#include<cstdio>#include<queue>#include<set>#include<cmath>

[POJ 1316] 树上的询问

[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1316 [算法] 点分治 由于边权较大,笔者在计算时使用了STL-set 注意当询问为0时,要输出"Yes" [代码] #include<bits/stdc++.h> using namespace std; #define MAXN 10010 #define MAXQ 110 struct Edge { int to,w,nxt; } e[MAXN<&

NKOJ P3815 树上的询问 (LCA 倍增)(复习距离倍增)

评测说明 : 1000ms 问题描述 现有一棵 n 个节点的树,树上每条边的长度均为 1.给出 m 个询问,每次询问两个节 点 x,y,求树上到 x,y 两个点距离相同的节点数量. 输入格式 第一个整数 n,表示树有 n 个点. 接下来 n-1 行每行两整数 a,b,表示从 a 到 b 有一条边. 接下来一行一个整数 m,表示有 m 个询问. 接下来 m 行每行两整数 x,y,询问到 x 和 y 距离相同的点的数量. 输出格式 共 m 行,每行一个整数表示询问的答案. 样例输入 7 1 2 1 

cqyz oj | 树上的询问 | 最近公共祖先

Description 现有一棵 n 个节点的棵, 树上每条边的长度均为 1. 给出 m 个询问, 每次询问两个节点 x,y, 求树上到 x,y 两个点距离相同的节点数量. Input 第一个整数 n, 表示树有 n 个点. 接下来 n-1 行每行两整数 a, b, 表示从 a 到 b 有一条边. 接下来一行一个整数 m, 表示有 m 个询问. 接下来 m 行每行两整数 x, y, 询问到 x 和 y 距离相同的点的数量. Output 共 m 行, 每行一个整数表示询问的答案. Sample

ac自动机fail树上按询问建立上跳指针——cf963D

解法看着吓人,其实就是为了优化ac自动机上暴力跳fail指针.. 另外这题对于复杂度的分析很有学习价值 /* 给定一个母串s,再给定n个询问(k,m) 对于每个询问,求出长度最小的t,使t是s的子串,且m作为子串在t中出现了m次 对多串建立ac自动机,然后用s去匹配,把所有询问的出现位置都用vector保存下来 然后对应每个询问的k进行更新答案 为了保证复杂度:在跳fail不能暴力向上跳,应该直接用一个指针pre跳到上一个带有询问的点 这样每次向上跳都让某个询问的vector更新进一个新的值 由