Loading

P5558 心上秋

Description

竟宁元年(前33年)正月,昭君出塞前一晚。

画师跌跌撞撞地来到昭君居住的宫殿。

 听说北国的那座城池
 被冬雪覆了终日
 等到故人长诀渐行渐远
 转眼已隔两世
            ——《心上秋》

如果再也不能相见的话,画师想着,他想给昭君留下些什么。

他想把他的画笔送给昭君。

昭君的宫殿里有 \(N\) 个房间,有 \(N-1\) 条道路连接这些房间。

画师现在在宫殿的入口大厅 \(S\) 房间,他依稀记得,昭君的房间在 \(T\) 号。

窗外,风雨大作,宫内忽暗忽明,一个人影也没有。

画家走进晦暗的通道,每条通道里的墙壁上都画有若干片枫叶,这是之前昭君让画师画的。昭君说,她特别喜欢秋天,尤其喜欢秋天的枫叶。

  并肩长谈过多少往事,恍然间黄昏已至    ——《心上秋》

通道内晦暗无比,画师想点亮通道内备好的蜡烛,他记得昭君有个习惯,每个通道内的蜡烛数量就是墙上枫叶的数量。昭君若想点燃一条通道内的蜡烛,就会全部点燃,此时昭君认为这条通道已被点亮,并且不会再点亮任何枫叶数少于这条通道的通道

这应该是最后一次来到这个地方了,画师想着,他要按昭君的习惯,走到昭君的房间。

现在画师想知道,他从宫殿大厅 \(S\) 走到昭君房间 \(T\),最多可以点亮多少通道。

一句话题意

给一棵树,带边权。多次询问,每次询问一条路径上的最长不下降子序列的长度。

\(N\le 30000,M\le 300000\)。边权 \(\in[1,5]\)

Solution

注意到边权极小,考虑从此突破。

对于一个序列,我们可以枚举起始值和终止值,然后求出满足值在起始值和终止值之间的最长不下降子序列。假如我们求出了若干序列的上述答案,那么我们就可以将题目所求路径分成若干序列,枚举序列交界处的值,那么有转移 \(dp_{i,x}=\max(dp_{i-1,y}+g_{i,y,x})\)。其中 \(dp_{i,x}\) 表示求完前 \(i\) 个序列,当前终止值为 \(x\) 的最长不下降子序列,\(g_{i,x,y}\) 表示对于序列 \(i\),起始值是 \(y\),终止值是 \(x\) 的最长不下降子序列。转移本质就是枚举当前序列的起始值是什么,而当前序列的起始值就是上一个序列的终止值。

但问题是对于不同的询问,预先要求的序列也是不同的。那么如何找到一些序列,使其可以去表示所有的路径呢?显然可以用树剖或者倍增。这里考虑使用倍增。

改变一下上面 \(g\) 数组的定义。设 \(g_{x,i,a,b}\) 表示从节点 \(x\) 开始,向上走 \(2^{i-1}\) 条边,起始值为 \(a\),终止值为 \(b\) 的最长不下降子序列。\(g\) 同样可以用倍增来得到,只需要枚举交界处的值是什么即可。\(g_{u,i,a,b}=\max(g_{u,i-1,a,c}+g_{v,i-1,c,b})\)\(v\) 表示 \(u\) 的第 \(2^{i-1}\) 级祖先)。

但是 \(x\to lca\to y\) 的过程中,\(x\to lca\) 是向上走没错,但是 \(lca\to y\) 是向下走,但我们只能维护向上的值。因此我们还需要求出从 \(y\) 向上走的最长不上升子序列,求法和求最长不下降子序列是类似的,在此不过多赘述。这里将最长不下降记为 \(g1\),最长不上升记为 \(g2\)

至此,我们就已完成了本题。首先我们要预处理出 \(g1\)\(g2\)。然后对于每条路径,从 \(x\)\(y\)\(lca\) 跳的时候,将若干 \(2\) 的次方长度的路径看作若干个序列,用上文求 \(dp\) 的做法转移(此时 \(dp\) 可以设为 \(dp_{x,i}\) 表示到了节点 \(x\),值为 \(i\) 的最长不下降(或上升)子序列)。最后枚举 \(lca\) 处的值是多少,\(ans=\max(dp^{x\to lca}_{lca,i}+dp^{y\to lca}_{lca,i})\)

注意一些小优化,比如说在求 \(lca\) 的时候就可以同时求 \(dp\)\(g1\)\(g2\) 数组可以合并称一个数组 \(g\),用 \(a\)\(b\) 的大小关系表示不下降还是不上升。时间复杂度 \(\mathcal O(5^3N\log N+5^2M\log N)\),但远远达不到。目前是最优解。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30005
using namespace std;
int n,m,tot,ans,f[N][20],dep[N],dp1[N][6],dp2[N][6],g[N][20][6][6];
struct node {int to,next,head,val;}a[N<<1];
int read()
{
	int res=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
	return res;
}
void add(int x,int y,int z)
{
	a[++tot].to=y;a[tot].val=z;a[tot].next=a[x].head;a[x].head=tot;
	a[++tot].to=x;a[tot].val=z;a[tot].next=a[y].head;a[y].head=tot;
}
void dfs(int x,int fa)
{
	f[x][0]=fa;dep[x]=dep[fa]+1;
	for (int i=a[x].head;i;i=a[i].next)
	{
		int y=a[i].to,z=a[i].val;
		if (y==fa) continue;
		for (int z1=1;z1<=z;++z1)
			for (int z2=z;z2<=5;++z2)
			{
				g[y][0][z1][z2]=1;
				g[y][0][z2][z1]=1;
			}
		dfs(y,x);
	}
}
int LCA(int x,int y)
{	
	for (int i=1;i<=5;++i)
		dp1[x][i]=dp2[y][i]=0;
	if (dep[x]>dep[y])
	{
        for (int i=14;i>=0;--i)
        {
            if (dep[f[x][i]]>=dep[y]) 
            {
            	for (int j=1;j<=5;++j)
            		dp1[f[x][i]][j]=0;
                for (int j=1;j<=5;++j)
                    for (int k=j;k<=5;++k)
                        dp1[f[x][i]][k]=max(dp1[f[x][i]][k],dp1[x][j]+g[x][i][j][k]);
                x=f[x][i];
            }
        }
    }
    else
    {
        for (int i=14;i>=0;--i)
        {
            if (dep[f[y][i]]>=dep[x]) 
            {
            	for (int j=1;j<=5;++j)
            		dp2[f[y][i]][j]=0;
                for (int j=1;j<=5;++j)
                    for (int k=1;k<=j;++k)
                        dp2[f[y][i]][k]=max(dp2[f[y][i]][k],dp2[y][j]+g[y][i][j][k]);
                y=f[y][i];
            }
        }
    }
	if (x==y) return x;
	for (int i=14;i>=0;--i)
	{
		if (f[x][i]!=f[y][i]) 
		{
			for (int j=1;j<=5;++j)
				dp1[f[x][i]][j]=dp2[f[y][i]][j]=0;
			for (int j=1;j<=5;++j)
				for (int k=1;k<=5;++k)
				{
					if (k>=j) dp1[f[x][i]][k]=max(dp1[f[x][i]][k],dp1[x][j]+g[x][i][j][k]);
					if (k<=j) dp2[f[y][i]][k]=max(dp2[f[y][i]][k],dp2[y][j]+g[y][i][j][k]);
				}
			x=f[x][i];y=f[y][i];
		}	
	}
	for (int j=1;j<=5;++j)
		dp1[f[x][0]][j]=dp2[f[y][0]][j]=0;
	for (int j=1;j<=5;++j)
		for (int k=1;k<=5;++k)
		{
			if (k>=j) dp1[f[x][0]][k]=max(dp1[f[x][0]][k],dp1[x][j]+g[x][0][j][k]);
			if (k<=j) dp2[f[y][0]][k]=max(dp2[f[y][0]][k],dp2[y][j]+g[y][0][j][k]);
		}
	return f[x][0];
}
int main()
{
	n=read();
	for (int i=1;i<n;++i)
	{
		int x,y,z;
		x=read();y=read();z=read();
		add(x,y,z);
	}
	dfs(1,0);
	for (int i=0;i<14;++i)
	{
		bool flag=false;
		for (int u=2;u<=n;++u)
		{
			int v=f[u][i];
			if (!f[v][i]) continue;
			f[u][i+1]=f[v][i];
			flag=true;
			for (int x=1;x<=5;++x)
				for (int z=x+1;z<=5;++z)
					for (int y=x;y<=z;++y)
					{
						g[u][i+1][x][z]=max(g[u][i+1][x][z],g[u][i][x][y]+g[v][i][y][z]);
						g[u][i+1][z][x]=max(g[u][i+1][z][x],g[u][i][z][y]+g[v][i][y][x]);
					}
			for (int x=1;x<=5;++x)
				g[u][i+1][x][x]=g[u][i][x][x]+g[v][i][x][x];
		}
		if (!flag) break;
	}
	m=read();
	for (int i=1;i<=m;++i)
	{
		int x,y;
		x=read();y=read();
		int lca=LCA(x,y);
		ans=0;
		for (int i=1;i<=5;++i)
			ans=max(ans,dp1[lca][i]+dp2[lca][i]);
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2023-10-14 08:49  Thunder_S  阅读(27)  评论(0编辑  收藏  举报