CF587C Duff in the Army


洛谷题面

题目大意

给定一棵树,树上每个点有一个权值,又有 \(q\) 个询问。

对于每一个询问 u v rk,让你求出点 \(u\) 与点 \(v\) 之间的路径上的点的前 \(rk\) 小。

题目分析

考虑树上倍增,比较无脑。

我们令 \(dis[i][j][k]\) 表示从点 \(i\) 向上跳 \(2^{j}-1\) 步后第 \(k\) 小的值,再用 \(w[i][j]\) 存储城市 \(i\) 里居住的人,按编号从小到大排序。

初始化时因为从点 \(i\) 向上跳 \(0(=2^0-1)\) 步后第 \(j\) 小就是 \(w[i][j]\),所以可以初始化出 \(dis\) 数组。

随后用类似于倍增 \(\mathbf{LCA}\) 父亲/祖先节点迭代的方法迭代即可,具体如下:

for(register int i=1;i<=n;i++)
{
	for(register int j=1;j<=10;j++)
	{
		dis[i][0][j]=w[i][j];
	}
}
	
for(register int j=1;j<=20;j++)
{
	for(register int i=1;i<=n;i++)
   	{
		for(register int k=1;k<=10;k++)
		{
			dis[i][j][k]=dis[fa[i][j-1]][j-1][k];
				
			dis[i][j][k+10]=dis[i][j-1][k];
		}
			
		sort(dis[i][j]+1,dis[i][j]+20+1);
	}
}

再来考虑怎么回答询问,不妨设当前询问为 x y rk

\(lca\) 表示 \(x,y\) 的树上最近公共祖先。

\(res1[~],res2[~]\) 初始化为很大的数。

\(x=lca\),我们倍增更新 \(y\to lca\) 的路径即可,将处理的前 \(10\) 小放在 \(res1[~]\) 中。

再将 \(w[x][i](1\le i\le 10)\) 的值放入 \(res1[~]\) 中也来更新,最后数组前 \(rk\) 个数中那些不为这个很大的数的数就是答案。

\(lca=y\) 和其他情况类似,详见代码。

注意时时排序!

代码

//2022/1/4

//2022/1/5

//2022/1/10

const int INF=0x3f3f3f3f;

const int ma=1e5+5;

struct Gragh
{
	int v;
	
	int nxt;
};

Gragh gra[ma<<1];

int head[ma],dep[ma],res1[21],res2[21];

int fa[ma][21],w[ma][21];

int dis[ma][21][21];
//dis[i][j][k]:i 向上跳 2^{j}-1 步后第 k 小的值 

int n,m,q;

int idx;

inline void add(int u,int v)
{
	gra[++idx].v=v;
	
	gra[idx].nxt=head[u];
	
	head[u]=idx;
}

inline void dfs(int now,int fath,int depth)
{
	fa[now][0]=fath;
	
	dep[now]=depth;
	
	for(register int i=1;i<=20;i++)
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];
	}
	
	for(register int i=head[now];i;i=gra[i].nxt)
	{
		int v=gra[i].v;
		
		if(v!=fath)
		{
			dfs(v,now,depth+1);
		}
	}
}

inline int LCA(int x,int y)
{
	if(dep[x]=0;i--)
	{
		if(dep[x]>=dep[y]+(1<=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			
			y=fa[y][i];
		}
	}
	
	return fa[x][0];
} 

inline void updata1(int x,int y)
{
	mst(res1,0x3f);
	
	for(register int i=20;i>=0;i--)
	{
		if(dep[x]>=dep[y]+(1<=0;i--)
	{
		if(dep[x]>=dep[y]+(1<