[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就行。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 10010
using namespace std;
set<int>s;
int n,m,sum,root,cnt;
int a[N],dic[N<<1],size[N],mx[N],deep[N];
bool vis[N],ans[N];
int to[N<<1],head[N],val[N<<1],nxt[N<<1],tot;
inline void add(int x,int y,int z)
{
    to[++tot]=y;
    val[tot]=z;
    nxt[tot]=head[x];
    head[x]=tot;
}
void getroot(int pos,int fa)
{
    mx[pos]=0,size[pos]=1;
    for(int i=head[pos];i;i=nxt[i])
    {
        if(to[i]==fa||vis[to[i]]) continue;
        getroot(to[i],pos);
        size[pos]+=size[to[i]];
        mx[pos]=max(mx[pos],size[to[i]]);
    }
    mx[pos]=max(mx[pos],sum-size[pos]);
    if(mx[root]>mx[pos]) root=pos;
}
void getdeep(int pos,int fa)
{
    size[pos]=1;
    dic[++cnt]=deep[pos];
    for(int i=head[pos];i;i=nxt[i])
    {
        if(to[i]==fa||vis[to[i]]) continue;
        deep[to[i]]=deep[pos]+val[i];
        getdeep(to[i],pos);
        size[pos]+=size[to[i]];
    }
}
void dispose(int pos)
{
    // cout << " Fuck && Shit " << endl ;
    vis[pos]=true;
    s.clear();
    s.insert(0);
    for(int i=head[pos];i;i=nxt[i])
    {
        // cout << " Fuck 2" << endl ;
        if(vis[to[i]]) continue;
        cnt=0; deep[to[i]]=val[i],getdeep(to[i],0);
        for(int j=1;j<=cnt;j++)
        {
            for(int k=1;k<=m;k++)
            {
                if(s.find(a[k]-dic[j])!=s.end()) ans[k]=1;
            }
        }
        for(int j=1;j<=cnt;j++) s.insert(dic[j]);
    }
    for(int i=head[pos];i;i=nxt[i])
    {
        if(vis[to[i]]) continue;
        sum=size[to[i]];
        root=0;
        getroot(to[i],0);
        dispose(root);
        // cout << " Fuck " << endl ;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int x,y,c;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&c);
        add(x,y,c),add(y,x,c);
    }
    // cout << "Fuck1" << endl ;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&a[i]);
        if(!a[i]) ans[i]=1;
    }
    mx[0]=1<<30;
    sum=n;
    // cout << "Fuck1" << endl ;
    getroot(1,0);
    // cout << "Fuck1" << endl ;
    dispose(root);
    // cout << "Fuck : " << m << endl ;
    for(int i=1;i<=m;i++)
    {
        printf("%s\n",ans[i]?"Yes":"No");
    }
    return 0;
}

小结:这种题更适合入门题。

原文地址:https://www.cnblogs.com/ShuraK/p/9097801.html

时间: 2024-10-29 10:21:36

[bzoj1316]树上的询问_点分治的相关文章

【点分治】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],ne

[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

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>

【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

[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更新进一个新的值 由