• BZOJ3653: 谈笑风生


    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。

    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


    1<=P<=N

    1<=K<=N

    N<=300000

    Q<=300000

     
    啊,摸蛤题。。。
    根据定义不难发现abc三点应该同在一条直链上,那么我们按b在a上面和下面2种情况分类
    第一种情况很简单,因为a子树除a之外的所有节点都可以作为c。
    第二种情况是一个偏序结构,b应该在a子树内部且b的深度不超过a的深度+k。因为问题可以离线,随便用棵平衡树和线段树合并一下就可以维护size信息了。
    因为内存比较富裕,我写的是线段树合并。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int maxn=300010;
    const int maxnode=maxn*20;
    struct Query {int k,id,next;}Q[maxn];
    int n,m,first2[maxn],first[maxn],next[maxn<<1],to[maxn<<1],dep[maxn],siz[maxn],e,cnt;
    void AddEdge(int u,int v) {
    	to[++e]=v;next[e]=first[u];first[u]=e;
    	to[++e]=u;next[e]=first[v];first[v]=e;
    }
    void AddQuery(int x,int k,int id) {
    	Q[++cnt]=(Query){k,id,first2[x]};first2[x]=cnt;
    }
    ll s[maxnode],ans[maxn];
    int ls[maxnode],rs[maxnode],root[maxn],ToT;
    void build(int& o,int l,int r,int p,int val) {
    	if(!o) o=++ToT;s[o]+=val;if(l==r) return;
    	int mid=l+r>>1;
    	if(p<=mid) build(ls[o],l,mid,p,val);
    	else build(rs[o],mid+1,r,p,val);
    }
    int merge(int x,int y) {
    	if(!x) return y;
    	if(!y) return x;
    	s[x]+=s[y];
    	ls[x]=merge(ls[x],ls[y]);
    	rs[x]=merge(rs[x],rs[y]);
    	return x;
    }
    ll query(int o,int l,int r,int x) {
    	if(!o) return 0;
    	if(l==r) return s[o];
    	int mid=l+r>>1;
    	if(x<=mid) return query(ls[o],l,mid,x);
    	return s[ls[o]]+query(rs[o],mid+1,r,x);
    }
    void dfs(int x,int fa) {
    	siz[x]=1;dep[x]=dep[fa]+1;
    	ren if(to[i]!=fa) dfs(to[i],x),siz[x]+=siz[to[i]],root[x]=merge(root[x],root[to[i]]);
    	for(int i=first2[x];i;i=Q[i].next) {
    		int k=Q[i].k,j=Q[i].id;
    		ans[j]=(ll)(siz[x]-1)*min(k,dep[x]-1);
    		ans[j]+=query(root[x],1,n,min(n,dep[x]+k));
    	}
    	build(root[x],1,n,dep[x],siz[x]-1);
    }
    int main() {
    	n=read();m=read();
    	rep(i,2,n) AddEdge(read(),read());
    	rep(i,1,m) {
    		int p=read(),k=read();
    		AddQuery(p,k,i);
    	}
    	dfs(1,0);
    	rep(i,1,m) printf("%lld
    ",ans[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    Python的文本数据
    Python
    正则表达式
    多数据库的链接
    工作感受月记 201902月
    女儿的出生
    工作感受月记 201901月
    工作感受月记 201812月
    工作感受月记 201811月
    工作感受月记 201809
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5508325.html
Copyright © 2020-2023  润新知