[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 MB
Submit: 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

Sample Output

3
1
3

HINT

 

Hint:边要加双向

    

posted @ 2019-02-12 02:41  wang9897  阅读(167)  评论(0编辑  收藏  举报