[BZOJ]3653: 谈笑风生
题解:
答案贡献分为两部分
第一部分为 b是a的祖先是 $ ans=min(dep[p]-1,k)*(num[p]-1) $
第二部分为 b是a的子孙节点 对于dfs建深度主席树 每个点的权值为子孙节点个数 然后查询对应dfs序区间 及相应的深度范围
#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:边要加双向