• luogu P5558 心上秋


    LINK:心上秋

    唐多令 宋 吴文英

    何处合成愁。离人心上秋。纵芭蕉,不雨也飕飕。都道晚凉天气好,有明月,怕登楼。
    
    年事梦中休。花空烟水流。燕辞归,客尚淹留。垂柳不萦裙带住。漫长是,系行舟。
    

    心上秋 笔下梅 笙中月。

    此题 求树上任意两点之间的最长不下降子序列 权值集合为{1~5}.

    (n<=30000,m<=300000)

    考虑暴力 把链抽出来然后暴力dp.最长不下降子序列的dp是nlogn的 所以总复杂度为nlognm.

    优化暴力 发现权值集合很小 把链抽出来之后 设f[i][j]表示到达第i个点权值为j的最长长度 多了一个五倍的常数不过复杂度O(n) 总复杂度nm.

    考虑优化 如果权值集合只有1,2怎么办?

    不妨考虑倍增 设f[i][j][k][w]表示从i跳到2^j的祖先经过的边的权值从k到w的最长不降子序列的长度。

    发现这个东西可以合并.

    对于所有数据我们可以倍增预处理出这个数组。

    对于某个询问(x,y) 我们先让x调到lca累计一个数组g[k]表示小于等于k的路径的最长长度 然后再从lca跳到y即可。

    跟保卫王国的倍增dp有点像 不过我没写过那道题的倍增dp 回来可以看一下 值得一提的是这道题没有修改 所以就简单了很多。

    我觉得树剖不太能写的样子 矩阵不是很好维护.

    我写的有点繁琐了。大体上有一个坑点:注意g数组的维护要维护某个区间的答案 初始化和转移要注意。

    在最后求答案的时候也注意由lca跳向y的时候要反着做. 数组下标也得反着.所以g数组也要有一个反着的值.

    通过这道题 我们发现可以扩展到序列上 如求某段序列的最长不下降子序列 也可以使用这种倍增的方法来做。

    可见倍增是优化dp的一种常用手段。

    const int MAXN=30010;
    int n,m,len,maxx,ans;
    int ql[MAXN],qr[MAXN],wl[MAXN],wr[MAXN],top1,top2;
    int g[MAXN][16][6][6],f[MAXN][16],Log[MAXN],d[MAXN],w[MAXN][6];
    int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],e[MAXN<<1];
    inline void add(int x,int y,int z)
    {
    	ver[++len]=y;
    	nex[len]=lin[x];
    	lin[x]=len;
    	e[len]=z;
    }
    inline void dfs(int x,int father)
    {
    	d[x]=d[father]+1;
    	f[x][0]=father;
    	rep(1,Log[d[x]],i)
    	{
    		f[x][i]=f[f[x][i-1]][i-1];
    		rep(1,maxx,k)
    			rep(1,maxx,w)
    			{
    				int s1=min(k,w),s2=max(k,w);
    				rep(s1,s2,l)g[x][i][k][w]=max(g[x][i][k][w],g[x][i-1][k][l]+g[f[x][i-1]][i-1][l][w]);
    			}
    	}
    	go(x)
    	{
    		if(tn==father)continue;
    		rep(e[i],maxx,j)rep(1,e[i],k)g[tn][0][j][k]=g[tn][0][k][j]=1;
    		dfs(tn,x);
    	}
    }
    inline void LCA(int x,int y)
    {
    	top1=top2=0;
    	if(d[x]>d[y])
    	{
    		fep(Log[d[x]],0,i)
    		{
    			if(d[f[x][i]]>=d[y])
    			{
    				ql[++top1]=x;
    				wl[top1]=i;
    				x=f[x][i];
    			}
    		}
    	}
    	if(d[y]>d[x])
    	{
    		fep(Log[d[y]],0,i)
    		{
    			if(d[f[y][i]]>=d[x])
    			{
    				qr[++top2]=y;
    				wr[top2]=i;
    				y=f[y][i];
    			}
    		}
    	}
    	if(x==y)return;
    	fep(Log[d[x]],0,i)
    	{
    		if(f[x][i]!=f[y][i])
    		{
    			ql[++top1]=x;
    			wl[top1]=i;
    			x=f[x][i];
    			qr[++top2]=y;
    			wr[top2]=i;
    			y=f[y][i];
    		}
    	}
    	ql[++top1]=x;
    	wl[top1]=0;
    	qr[++top2]=y;
    	wr[top2]=0;
    }
    inline void get_ans()
    {
    	ans=0;
    	rep(1,top1,i)
    	{
    		rep(1,maxx,j)w[i][j]=0;
    		rep(1,maxx,j)
    			rep(1,j,k)w[i][j]=max(w[i][j],w[i-1][k]+g[ql[i]][wl[i]][k][j]);
    	}
    	int l=top2;
    	rep(top1+1,top2+top1,i)
    	{
    		rep(1,maxx,j)w[i][j]=0;
    		rep(1,maxx,j)
    			rep(1,j,k)w[i][j]=max(w[i][j],w[i-1][k]+g[qr[l]][wr[l]][j][k]);
    		--l;
    	}
    	rep(1,maxx,i)ans=max(ans,w[top1+top2][i]);
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	get(n);
    	rep(2,n,i)
    	{
    		int x,y,z;
    		get(x);get(y);get(z);
    		maxx=max(maxx,z);
    		add(x,y,z);add(y,x,z);
    		Log[i]=Log[i>>1]+1;
    	}
    	dfs(1,0);
    	get(m);
    	rep(1,m,i)
    	{
    		int x,y;
    		get(x);get(y);
    		LCA(x,y);
    		get_ans();
    		put(ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue之下拉菜单Dropdown的使用
    elinput输入框的readonly属性
    通过CollectionUtils工具类判断集合是否为空,通过StringUtils工具类判断字符串是否为空
    前端与后端之间参数的传递与接收和@RequestBody,@Requestparam,@Param三个注解的使用
    为什么数据库能查出两条id相同的数据
    js中函数调用时,对参数个数和类型没有要求
    避免创建不需要的对象,消除过期对象的引用
    避免使用终结方法
    避免使用终结方法
    快速排序
  • 原文地址:https://www.cnblogs.com/chdy/p/12597923.html
Copyright © 2020-2023  润新知