• 【BZOJ3653】谈笑风生(线段树合并)


    点此看题面

    大致题意: 给定一棵树,定义祖先节点比后代节点“不知道高明到哪里去了”,树上距离不超过(k)的两点“谈笑风生”。每次询问给出(a,k),求有多少对((b,c))满足(a,b)都比(c)不知道高明到哪里去了且(a)(b)谈笑风生。

    分类讨论

    考虑(a)(b)都比(c)不知道高明到哪里去了,因此二者必然有一个是另一个的祖先。

    • (b)是祖先:合法的(b)的个数为(min{dep_a,k})(根节点(dep)(0)),合法的(c)的个数为(Sz_a-1)
    • (a)是祖先:相当于求(a)子树内所有深度不超过(dep_a+k)的点的(Sz-1)的总和。要维护子树信息,显然可以用线段树合并,以深度为下标,维护(Sz-1)的和。

    因此,我们离线把询问扔到每个节点上,(dfs)一遍即可求出答案。

    (写完才发现其实不用离线,完全可以在线搞。。。)

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    #define LN 20
    #define LL long long 
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,ee,lnk[N+5],p[N+5],Sz[N+5],Rt[N+5];struct edge {int to,nxt;}e[N<<1];
    struct Qry {int p,k;};vector<Qry> q[N+5];long long ans[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    }F;
    class SegmentTree//线段树合并
    {
    	private:
    		#define PU(x) (O[x].V=O[O[x].S[0]].V+O[O[x].S[1]].V)
    		int Nt;struct node {LL V;int S[2];}O[N*LN<<1];
    	public:
    		I void U(CI x,CI v,CI l,CI r,int& rt)//单点修改
    		{
    			if(!rt&&(rt=++Nt),l==r) return (void)(O[rt].V+=v);int mid=l+r>>1;
    			x<=mid?U(x,v,l,mid,O[rt].S[0]):U(x,v,mid+1,r,O[rt].S[1]),PU(rt);
    		}
    		I LL Q(CI L,CI R,CI l,CI r,CI rt)//区间求和
    		{
    			if(!rt||(L<=l&&r<=R)) return O[rt].V;int mid=l+r>>1;
    			return (L<=mid?Q(L,R,l,mid,O[rt].S[0]):0)+(R>mid?Q(L,R,mid+1,r,O[rt].S[1]):0);
    		}
    		I void Merge(int& x,CI y,CI l,CI r)//线段树合并
    		{
    			if(!x||!y) return (void)(x|=y);if(O[++Nt]=O[x],x=Nt,l==r) return (void)(O[x].V+=O[y].V);
    			int mid=l+r>>1;Merge(O[x].S[0],O[y].S[0],l,mid),Merge(O[x].S[1],O[y].S[1],mid+1,r),PU(x);
    		}
    }S;
    I void dfs(CI x=1,CI lst=0)
    {
    	RI i;for(Sz[x]=1,i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    		(p[e[i].to]=p[x]+1,dfs(e[i].to,x),S.Merge(Rt[x],Rt[e[i].to],0,n-1),Sz[x]+=Sz[e[i].to]);//统计子树信息
    	vector<Qry>::iterator it;for(it=q[x].begin();it!=q[x].end();++it)//枚举询问
    		ans[it->p]=1LL*min(p[x],it->k)*(Sz[x]-1)+S.Q(p[x],min(p[x]+it->k,n-1),0,n-1,Rt[x]);//考虑b为祖先和a为祖先的答案
    	S.U(p[x],Sz[x]-1,0,n-1,Rt[x]);//在线段树上更新当前点信息
    }
    int main()
    {
    	RI Qt,i,x,y;for(F.read(n),F.read(Qt),i=1;i^n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
    	for(i=1;i<=Qt;++i) F.read(x),F.read(y),q[x].push_back((Qry){i,y});//离线(其实没必要)
    	for(dfs(),i=1;i<=Qt;++i) F.writeln(ans[i]);return F.clear(),0;
    }
    
  • 相关阅读:
    gmap 整理
    记录一次mybatis genertor使用以及mapper扫描遇见的问题
    mysql 记录
    NOIP2018Day1!!!题目出炉!!!
    图论——倍增求LCA
    干货系列——模板 之 图论1
    数学专题1
    动态规划——背包问题1:01背包
    图论——最短路——Dijkstra算法
    数据结构——并查集
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3653.html
Copyright © 2020-2023  润新知