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

主席树板子了

首先看到这个暴力异常的题面,感觉做了这道题的会没命的

首先先考虑\(b\)在\(a\)子树内部的情况,这个样子的话我们需要知道子树内部所有深度小于等于\(deep[a]+k\)的点带来的贡献是是多少,由于这里的\(a,b,c\)都不能是同一个节点,所以这里的贡献就是子树大小减1,同时\(b\)也不能是\(a\)

之后按照深度建主席树就好了,权值是子树大小减1,我们就可以快速的查询点子树内部所有距离它的点小于等于\(k\)的点带来的权值和了

之后在考虑子树外部的情况,由于\(a,b\)都是\(c\)的祖先,所以\(b\)不在\(a\)子树内部的话就必定在\(a\)到根的路径上,而\(c\)也只能在\(a\)的子树里选取,所以权值是\(sum[a]-1\),至于\(b\)的选择我们就可以从\(a\)往上的\(k\)个节点

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 300005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL long long
struct E
{
    int v,nxt;
}e[maxn<<1];
int l[maxn*20],r[maxn*20];
LL d[maxn*20];
int cnt,tot;
int build(int x,int y)
{
    int root=++cnt;
    if(x==y) return root;
    int mid=x+y>>1;
    l[root]=build(x,mid);
    r[root]=build(mid+1,y);
    return root;
}
int change(int pre,int x,int y,int pos,LL val)
{
    int root=++cnt;
    d[root]=d[pre]+val;
    if(x==y) return root;
    l[root]=l[pre],r[root]=r[pre];
    int mid=x+y>>1;
    if(pos<=mid) l[root]=change(l[pre],x,mid,pos,val);
    else r[root]=change(r[pre],mid+1,y,pos,val);
    return root;
}
LL query(int pos,int x,int y,int xx,int yy)
{
    if(xx<=x&&yy>=y) return d[pos];
    int mid=x+y>>1;
    if(yy<=mid) return query(l[pos],x,mid,xx,yy);
    if(xx>mid) return query(r[pos],mid+1,y,xx,yy);
    return query(l[pos],x,mid,xx,yy)+query(r[pos],mid+1,y,xx,yy);
}
int head[maxn],deep[maxn],to[maxn],rt[maxn],_to[maxn];
LL p[maxn],sum[maxn];
int n,Q,num,maxdep;
inline void add_edge(int x,int y)
{
    e[++num].v=y;
    e[num].nxt=head[x];
    head[x]=num;
}
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘)
      x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
void dfs(int x)
{
    sum[x]=1;
    to[x]=++tot;
    _to[tot]=x;
    for(re int i=head[x];i;i=e[i].nxt)
    if(!deep[e[i].v])
    {
        deep[e[i].v]=deep[x]+1;
        p[e[i].v]=p[x]+1;
        dfs(e[i].v);
        sum[x]+=sum[e[i].v];
    }
    maxdep=max(maxdep,deep[x]);
}
int main()
{
    n=read();Q=read();
    int x,y;
    for(re int i=1;i<n;i++)
    {
        x=read(),y=read();
        add_edge(x,y),add_edge(y,x);
    }
    deep[1]=1;
    dfs(1);
    rt[0]=build(1,maxdep);
    for(re int i=1;i<=n;i++)
        rt[i]=change(rt[i-1],1,maxdep,deep[_to[i]],sum[_to[i]]-1);
    LL k;
    while(Q--)
    {
        x=read(),k=read();
        LL ans=0;
        ans=min(p[x],k)*(sum[x]-1);
        y=to[x]+sum[x]-1;
        if(deep[x]!=maxdep)
            ans+=query(rt[y],1,maxdep,deep[x]+1,min(deep[x]+k,maxdep))-query(rt[to[x]-1],1,maxdep,deep[x]+1,min(deep[x]+k,maxdep));
        printf("%lld",ans),putchar(10);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/asuldb/p/10206216.html

时间: 2024-07-30 20:06:02

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

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

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

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 【[湖南集训]谈笑风生】

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\)表示深度,

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

湖南集训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 语录 "这里是制造快乐,编织幸福的梦工厂!才不是你们想的什么奇怪的工厂呢!哼!" 这是你来到这个奇妙的地方所听到的第一句话,竟然还是一头身长三米的狗熊发出来的. 一开始看到梦工厂这块牌匾时,你心里想的是什么呢?如果真的是洋溢着青涩而纯洁的 梦想,那就太棒了! 初来乍到,狗熊先生决定给你先大致地讲解一下工厂的运作过程

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

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

湖南省队集训 -- 1

不知道为什么今天状态 大 好 可能是暴力分比较简单吧,来认真写一下总结 开局睡了1h-- 然后考虑a题,发现特殊性:因为最后的和一定是10^n的,最后两个数一定是后面一段全是0,前面一段两个数的和是⑨,0和⑨中间夹一对和为10 然后脑补正解不能,暴力枚举一下好像可以,就是判断是在不好写 然后考虑b题,看N^2算法,脑玩了一下发现距离是不会变得,如果钦定了一个点建站第1~n-1个点产生的权值是不会变得,最后一个点的距离也变不了,唯一得变量需求要乘距离,然后把剩下的不变量加起来,这就是个kx+b的形

[BZOJ3653]谈笑风生

试题描述 设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)满足:1. a.b和 c为 T 中三个不同的点,且 a