【CF516D】Drazil and Morning Exercise(换根DP预处理+暴力双指针)

点此看题面

  • 给定一棵(n)个点的有根树,每条边有一个边权。
  • 定义(f(x)=max_{i=1}^ndist(x,i))
  • (q)组询问,每次给出一个数(l),求最大的满足(max_{xin S}f(x)-min_{xin S}f(x)le l)的连通块(S)的大小。
  • (nle10^5,qle50)

换根(DP)预处理

首先我们要预处理所有(f(x))

这应该是一个非常简单的换根(DP),首先第一次(dfs)预处理出每个点的最长链、次长链,然后第二次(dfs)记录一个子树外的最长路径再扫一遍即可。

暴力双指针

发现(q)非常小,因此考虑对于每次询问直接上双指针跑一遍。

我们从大到小枚举左端点,移动右端点以满足最大值减最小值不超过(l),然后就是要询问左端点所在连通块的大小。

看起来点既会加入又会删去,貌似要写个(LCT)。(当然想写(LCT)也没关系啦)

不过我们考虑这题中(f(x))的实际意义,其实(f(x))越大的点就离重心越远。

而我们每次删掉的是(f(x))最大的点,它必然在边缘处,故该点是否存在并不影响现在及以后任一时刻的连通性,只要把所在连通块大小减(1)即可。

代码:(O(nlogn+nqalpha(n)))

#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 100000
#define LL long long
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
using namespace std;
int n,p[N+5],Ex[N+5],ee,lnk[N+5];struct edge {int to,nxt,v;}e[N<<1];
int fa[N+5],g[N+5];I int getfa(CI x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}
namespace TreeDP//换根DP
{
	LL Mx[N+5],Sx[N+5];int P[N+5];I void dfs1(CI x,CI lst=0)//第一遍dfs预处理最长链次长链
	{
		LL t;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs1(e[i].to,x),
			(t=Mx[e[i].to]+e[i].v)>Mx[x]?(Sx[x]=Mx[x],Mx[x]=t,P[x]=e[i].to):Sx[x]<t&&(Sx[x]=t));
	}
	LL f[N+5];I void dfs2(CI x,CI lst=0,Con LL& M=0)//第二遍dfs换根DP求出f(x)
	{
		f[x]=max(Mx[x],M);for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
			(dfs2(e[i].to,x,max(M,P[x]^e[i].to?Mx[x]:Sx[x])+e[i].v),0);
	}
}
using namespace TreeDP;
I bool cmp(CI x,CI y) {return f[x]<f[y];}//根据f(x)大小排序
int main()
{
	RI Qt,i,x,y,z;for(scanf("%d",&n),i=1;i^n;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
	LL t;for(dfs1(1),dfs2(1),i=1;i<=n;++i) p[i]=i;sort(p+1,p+n+1,cmp);
	RI ans;scanf("%d",&Qt);W(Qt--)
	{
		for(scanf("%lld",&t),i=1;i<=n;++i) fa[i]=i,g[i]=Ex[i]=0;//清空
		for(ans=0,x=y=n;y;ans=max(ans,g[p[y]]),--y)//从大到小枚举左端点
		{
			W(f[p[x]]-f[p[y]]>t) --g[getfa(p[x])],Ex[p[x--]]=0;g[p[y]]=Ex[p[y]]=1;//双指针,维护右端点
			for(i=lnk[p[y]];i;i=e[i].nxt) Ex[e[i].to]&&(fa[z=getfa(e[i].to)]=p[y],g[p[y]]+=g[z]);//更新连通性
		}printf("%d
",ans);
	}return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF516D.html