题解 P3899 【[湖南集训]谈笑风生】

Luogu智能推荐给我搞的这个题啊,亦可赛艇!

题目链接

Solution [湖南集训]谈笑风生

题目大意:和Wallace谈笑风生,给定一棵有根树,多次询问给定点\(p\)和限制\(k\),求有多少对有序三元组\((p,b,c)\)满足\(p,b\)均为\(c\)的祖先且\(p,b\)间距离不超过\(k\)

主席树,树上差分



分析:

首先我们分类讨论一下

如果点\(b\)在点\(p\)上方时,有\(min(dep[p] - 1,k)\times(siz[p]-1)\),\(dep\)表示深度,\(siz\)表示子树大小,乘法原理显然

关键是当点\(b\)在点\(p\)子树内是,对于一个确定的点\(b\),显然点\(c\)可以选择的数量就是\(siz[b]-1\)

问题变成了,给定一棵树,每个点有一个点权,求一个点子树内与它距离不超过\(k\)的点的权值之和

首先我们如果以深度为下标建一棵线段树,假设我们知道一个点子树的线段树的话查询就是区间求和问题,但是暴力计算时空复杂度均无法承受

我们考虑子树的性质,如果求\(dfs\)序的话,一棵子树内点的\(dfs\)序一定是连续的,因此我们可以用主席树来计算,差分即可得到一个点子树的线段树

#include <cstdio>
#include <cctype>
#include <vector>
using namespace std;
const int maxn = 3e5 + 100;
typedef long long ll;
inline int read(){
    int x = 0;char c = getchar();
    while(!isdigit(c))c = getchar();
    while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    return x;
}
namespace ST{
    const int maxnode = maxn * 30;
    struct Node{
        int ls,rs;
        ll val;
    }tree[maxnode];
    int root[maxn],tot;
    inline void pushup(int root){tree[root].val = tree[tree[root].ls].val + tree[tree[root].rs].val;}
    inline void add(int last,int &root,int pos,int val,int l = 1,int r = 3e5){
        root = ++tot;
        if(l == r){
            tree[root].val = tree[last].val + val;
            return;
        }
        int mid = (l + r) >> 1;
        if(pos <= mid)tree[root].rs = tree[last].rs,add(tree[last].ls,tree[root].ls,pos,val,l,mid);
        else tree[root].ls = tree[last].ls,add(tree[last].rs,tree[root].rs,pos,val,mid + 1,r);
        pushup(root);
    }
    inline ll query(int last,int root,int a,int b,int l = 1,int r = 3e5){
        if(a <= l && b >= r)return tree[root].val - tree[last].val;
        int mid = (l + r) >> 1;ll res = 0;
        if(a <= mid)res += query(tree[last].ls,tree[root].ls,a,b,l,mid);
        if(b >= mid + 1)res += query(tree[last].rs,tree[root].rs,a,b,mid + 1,r);
        return res;
    }
}using namespace ST;
vector<int> G[maxn];
inline void addedge(int from,int to){G[from].push_back(to);}
int dep[maxn],dfn[maxn],rnk[maxn],siz[maxn],dfs_tot,n,q;
inline void dfs(int u,int faz = -1){
    siz[u] = dep[1] = 1;
    dfn[u] = ++dfs_tot;
    rnk[dfs_tot] = u;
    for(int v : G[u]){
        if(v == faz)continue;
        dep[v] = dep[u] + 1;
        dfs(v,u);
        siz[u] += siz[v];
    }
}
int main(){
    n = read(),q = read();
    for(int u,v,i = 1;i < n;i++)
        u = read(),v = read(),addedge(u,v),addedge(v,u);
    dfs(1);
    for(int i = 1;i <= n;i++)
        add(root[i - 1],root[i],dep[rnk[i]],siz[rnk[i]] - 1);
    for(int a,k,i = 1;i <= q;i++){
        a = read(),k = read();
        ll ans = min(dep[a] - 1,k) * ll(siz[a] - 1);
        ans += query(root[dfn[a]],root[dfn[a] + siz[a] - 1],dep[a] + 1,dep[a] + k);
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/colazcy/p/11840239.html

时间: 2024-08-30 13:23:46

题解 P3899 【[湖南集训]谈笑风生】的相关文章

P3899 [湖南集训]谈笑风生

P3899 [湖南集训]谈笑风生 题目大意 n个节点的树,q次查询,每次查询给出a,k求三元组的数量(a,b,c),(a,b,c)的定义为:a.b均为c的祖先且距离<=k 离线,启发式合并线段树,长链剖分当然都能过这题 这里讲讲主席树的做法 dfs序建树 a为b的祖先时 查询a子树内深度<=dep[a]+k的节点的子树和 b为a祖先时 乘法原理就好 My complete code: #include<cstdio> #include<string> #include&

P3899 [湖南集训]谈笑风生 (线段树合并)

题目描述 设 T 为一棵有根树,我们做如下的定义: • 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称“a 比 b 不知道高明到哪里去了”. • 设 a 和 b 为 T 中的两个不同节点.如果 a 与 b 在树上的距离不超过某个给定常数 x,那么称“a 与 b 谈笑风生”. 给定一棵 n 个节点的有根树 T,节点的编号为 1 ∼ n,根节点为 1 号节点.你需要回答 q 个询问,询问给定两个整数 p 和 k,问有多少个有序三元组 (a; b; c) 满足: a. b

[湖南集训] 谈笑风生 (主席树)

[湖南集训] 谈笑风生 题目描述 设 T 为一棵有根树,我们做如下的定义: ? 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称"a 比 b 不知道高明到哪里去了". ? 设 a 和 b 为 T 中的两个不同节点.如果 a 与 b 在树上的距离不超过某个给定常数 x,那么称"a 与 b 谈笑风生". 给定一棵 n 个节点的有根树 T,节点的编号为 1 - n,根节点为 1 号节点.你需要回答 q 个询问,询问给定两个整数 p 和 k,问有多

【[湖南集训]谈笑风生】

主席树板子了 首先看到这个暴力异常的题面,感觉做了这道题的会没命的 首先先考虑\(b\)在\(a\)子树内部的情况,这个样子的话我们需要知道子树内部所有深度小于等于\(deep[a]+k\)的点带来的贡献是是多少,由于这里的\(a,b,c\)都不能是同一个节点,所以这里的贡献就是子树大小减1,同时\(b\)也不能是\(a\) 之后按照深度建主席树就好了,权值是子树大小减1,我们就可以快速的查询点子树内部所有距离它的点小于等于\(k\)的点带来的权值和了 之后在考虑子树外部的情况,由于\(a,b\

湖南集训day1

早上八点20开始考试...题目自己做的并不好: 满分三百分 拿到了40: t1暴力的话是个简单的dfs,能拿到40分: t2暴力的话可以拿到10分: t3暴力的话可以拿到30分: 但是我t1文件输入写错了: 所以只拿到了40分: 题目难度仅仅是略高于noip,感觉自己还是菜: 考完后尧神是t2 60分是个shabi网络流......shabi般的lsj来这种板子都没看出来: 再想一下的话,其实今天可以拿的分应该有40+70+30=140的: 140也不低了: 所以还是要好好打好基础吧: 下午评卷

2015湖南集训DAY8——梦工厂

梦工厂 (yume.cpp/c/pas) Time Limit: 1 s Memory Limit: 128 M 问题描述 「有时候用烂了的名字也会别有深意」 --摘自EN 语录 "这里是制造快乐,编织幸福的梦工厂!才不是你们想的什么奇怪的工厂呢!哼!" 这是你来到这个奇妙的地方所听到的第一句话,竟然还是一头身长三米的狗熊发出来的. 一开始看到梦工厂这块牌匾时,你心里想的是什么呢?如果真的是洋溢着青涩而纯洁的 梦想,那就太棒了! 初来乍到,狗熊先生决定给你先大致地讲解一下工厂的运作过程

[题解列表] GDUT-ACM集训题目

放在Virtual Judge上的专题: p { margin-bottom: 0.25cm; line-height: 115% } a:link { } [题解][树状数组] POJ 2352 - Stars https://www.cnblogs.com/Kaidora/p/10389073.html [题解] [BFS] POJ 1426 - Find The Multiple https://www.cnblogs.com/Kaidora/p/10392293.html 放在CodeF

2018.8.17 题解 2018暑假集训之编辑距离

应该是一个很经典的题目了吧 上题面描述 概念 字符串的编辑距离,又称为Levenshtein距离,由俄罗斯的数学家Vladimir Levenshtein在1965年提出.是指利用字符操作,把字符串A转换成字符串B所需要的最少操作数.其中,字符操作包括: 删除一个字符     a) Insert a character 插入一个字符     b) Delete a character 修改一个字符     c) Replace a character 例如对于字符串"if"和"

Noip前的大抱佛脚----数据结构

数据结构 线段树 注意:空间开4倍 神奇标记 From8.26 Test_zsy(CPU监控) 如果一个点权为\(val\)的点被打上了\((a,b)\)标记,那么他的实际点权为\(max(a+val,b)\) 干啥滴? 标记不下放 区间加标记不下放,维护区间max或者最大值 方法是当前\(tag\)维护当前区域标记,\(t\)维护左右儿子的\(max+tag[now]\),并没有快多少,如果仍然忘记见提交记录 并查集 维护二分图 并查集每个点维护是否要改颜色,然后按秩合并/按大小合并即可 实际