[BZOJ]3653: 谈笑风生
题解:
答案贡献分为两部分
第一部分为 b是a的祖先是 ans=min(dep[p]−1,k)∗(num[p]−1)
第二部分为 b是a的子孙节点 对于dfs建深度主席树 每个点的权值为子孙节点个数 然后查询对应dfs序区间 及相应的深度范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <stack> #include <queue> #include <cmath> #include <set> #include <map> #define mp make_pair #define pb push_back #define pii pair<int,int> #define link(x) for(edge *j=h[x];j;j=j->next) #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) const int MAXN=3e5+10; const double eps=1e-8; #define ll long long using namespace std; struct edge{ int t;edge*next;}e[MAXN<<1],*h[MAXN],*o=e; void add( int x, int y){o->t=y;o->next=h[x];h[x]=o++;} ll read(){ ll x=0,f=1; char ch= getchar (); while (! isdigit (ch)){ if (ch== '-' )f=-1;ch= getchar ();} while ( isdigit (ch))x=x*10+ch- '0' ,ch= getchar (); return x*f; } int n,m; int dep[MAXN],num[MAXN],fa[MAXN],p[MAXN],fp[MAXN],cnt; void dfs( int x, int pre, int deep){ dep[x]=deep+1;num[x]=1;fa[x]=pre;p[x]=++cnt;fp[p[x]]=x; link(x) if (j->t!=pre)dfs(j->t,x,deep+1),num[x]+=num[j->t]; } typedef struct node{ int l,r;ll sum; }node; node d[MAXN*21]; int cnt1,rt[MAXN]; void update( int &x, int y, int l, int r, int t, int k){ x=++cnt1;d[x]=d[y];d[x].sum+=k; if (l==r) return ; int mid=(l+r)>>1; if (t<=mid)update(d[x].l,d[y].l,l,mid,t,k); else update(d[x].r,d[y].r,mid+1,r,t,k); } ll ans1; void query( int x, int y, int l, int r, int ql, int qr){ if (ql<=l&&r<=qr){ans1+=(d[y].sum-d[x].sum); return ;} int mid=(l+r)>>1; if (ql<=mid)query(d[x].l,d[y].l,l,mid,ql,qr); if (qr>mid)query(d[x].r,d[y].r,mid+1,r,ql,qr); } int main(){ n=read();m=read(); int u,v; inc(i,2,n)u=read(),v=read(),add(u,v),add(v,u); dfs(1,0,0); inc(i,1,cnt)update(rt[i],rt[i-1],1,n,dep[fp[i]],num[fp[i]]-1); while (m--){ u=read();v=read(); ll ans=1ll*min(dep[u]-1,v)*(num[u]-1); ans1=0;query(rt[p[u]],rt[p[u]+num[u]-1],1,n,dep[u]+1,min(dep[u]+v,n)); printf ( "%lld\n" ,ans1+ans); } return 0; } |
3653: 谈笑风生
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1278 Solved: 549
[Submit][Status][Discuss]
Description
设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为p 号节点;
2. a和b 都比 c不知道高明到哪里去了;
3. a和b 谈笑风生。这里谈笑风生中的常数为给定的 k。
Input
第一行含有两个正整数n和q,分别代表有根树的点数与询问的个数。
接下来n - 1行,每行描述一条树上的边。每行含有两个整数u和v,代表在节点u和v之间有一条边。
接下来q行,每行描述一个操作。第i行含有两个整数,分别表示第i个询问的p和k。
1<=P<=N
1<=K<=N
N<=300000
Q<=300000
Output
输出 q 行,每行对应一个询问,代表询问的答案。
Sample Input
5 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3
Sample Output
3
1
3
1
3
HINT
Hint:边要加双向
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有调度器的协程不是好协程,零基础深入浅出 C++20 协程
· 别做抢活的导演:代码中的抽象层次原则
· 从 Redis 客户端超时到 .NET 线程池挑战
· C23和C++26的#embed嵌入资源指南
· 「EF Core」框架是如何识别实体类的属性和主键的
· 阿里巴巴为什么禁止超过3张表join?
· 博客园众包线下沙龙第1期:云栖开发者基地,共建技术新天地
· 让 AI 帮我部署网站,太方便了!
· 别做抢活的导演:代码中的抽象层次原则
· .NET周刊【7月第1期 2025-07-06】
2018-02-12 几种简单莫队