• CF516DDrazil and Morning Exercise【树上差分,倍增】


    正题

    题目链接:https://www.luogu.com.cn/problem/CF516D


    题目大意

    给出一棵\(n\)个点的树,定义\(f(x)\)表示距离点\(x\)最远的点的距离,\(q\)次询问给出一个\(k\),要求一个最大的连通块满足连通块中所有点的\(f(x)\)最大最小差值不能超过\(k\)

    \(1\leq n\leq 10^5,1\leq q\leq 50\)


    解题思路

    我们找到\(f(x)\)最小的点作为根,那么肯定有每一个点的\(f(x)\)都不小于其父节点的,具体原理是因为每个点出发的最长简单路端点肯定是直径的某一端,而这个最小的\(f(x)\)可以视为直径的中点。

    那么对于每个询问我们就直接枚举所有点,然后往上倍增到一个深度最浅的祖先满足\(f(x)-f(z)\leq k\),然后用树上差分给\(z\leftrightarrow x\)路径上所有点的权值\(+1\),最后求一个点权最大的点就好了。

    时间复杂度:\(O(qn\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e5+10;
    struct node{
    	ll to,next,w;
    }a[N<<1];
    ll n,q,tot,rt,ls[N],len[N];
    ll f[N][18],dep[N],c[N];
    void addl(ll x,ll y,ll w){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;a[tot].w=w;
    	return;
    }
    void dfs(ll x,ll fa){
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;dfs(y,x);
    		len[x]=max(len[x],len[y]+a[i].w);
    	}
    	return;
    }
    void calc(ll x,ll fa,ll mxl){
    	ll mx=mxl,mi=0;len[x]=max(len[x],mxl);
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		if(mx<len[y]+a[i].w)mi=mx,mx=len[y]+a[i].w;
    		else if(mi<len[y]+a[i].w)mi=len[y]+a[i].w;
    	}
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		if(mx==len[y]+a[i].w)calc(y,x,mi+a[i].w);
    		else calc(y,x,mx+a[i].w);
    	}
    	return;
    }
    void sfd(ll x,ll fa){
    	dep[x]=dep[fa]+1;
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		f[y][0]=x;sfd(y,x);
    	}
    	return;
    }
    void carc(ll x,ll fa){
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		carc(y,x);c[x]+=c[y];
    	}
    	return;
    }
    void Query(ll d){
    	memset(c,0,sizeof(c));len[0]=-1e18;
    	for(ll i=1;i<=n;i++){
    		ll x=i;
    		for(ll j=17;j>=0;j--)
    			if(len[i]-len[f[x][j]]<=d)x=f[x][j];
    		c[i]++;c[f[x][0]]--;
    	}
    	carc(rt,0);ll ans=0;
    	for(ll i=1;i<=n;i++)ans=max(ans,c[i]);
    	printf("%lld\n",ans);return;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	for(ll i=1,x,y,w;i<n;i++){
    		scanf("%lld%lld%lld",&x,&y,&w);
    		addl(x,y,w);addl(y,x,w);
    	}
    	dfs(1,0);
    	calc(1,0,0);len[0]=1e18;
    	for(ll i=1;i<=n;i++)
    		if(len[i]<len[rt])rt=i;
    	sfd(rt,0);
    	for(int j=1;j<18;j++)
    		for(int i=1;i<=n;i++)
    			f[i][j]=f[f[i][j-1]][j-1];
    	scanf("%lld",&q);
    	while(q--){
    		ll x;
    		scanf("%lld",&x);
    		Query(x);
    	}
    	return 0;
    }
    
  • 相关阅读:
    【上线复盘】20190329-一个刷新数据的接口是如何导致几十万的订单数据错误
    VS------csc.exe已停止工作解决方法
    SQLServer------存储过程的使用
    SQLServer------聚集索引和非聚集索引的区别
    SQLServer------Sql Server性能优化辅助指标SET STATISTICS TIME ON和SET STATISTICS IO ON
    SQLServer------如何快速插入几万条测试数据
    SQLServer------如何让标识列重新开始计算
    SQLServer------begin tran/commit tran事务的使用方法
    SQLServer------插入数据时出现IDENTITY_INSERT错误
    Spring----Spring Boot Rest的使用方法
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16469621.html
Copyright © 2020-2023  润新知