zoukankan      html  css  js  c++  java
  • [LGOJ5558]心上秋(倍增)

    题意

    给一颗边带权的树,边权为1~5,多次询问树上某条路径组成的边权序列的LIS

    思路

    假设已知边权序列,设(f_{i,j})表示处理了前(i)个数,当前(LIS)中的最后一个数为(j)时的(LIS)长度,显然有(f_{i,j}=max(f_{i-1,k}+1),(kleq j)),由于边权为1~5,这个算法一次是(O(n))

    通过上述对(f)的处理的思路就可以得到倍增法

    (fa_{rt,i})表示从(rt)向上跳(2^i)步到的节点
    (g_{rt,i,j,k}),表示从(rt)向上跳(2^i)步得到的边权序列,当前(LIS)中的最小数为(j),最大数为(k)时的(LIS)长度

    有转移方程

    (g_{rt,i,j,k}=max(g_{rt,i-1,j,p}+g_{fa_{rt,i-1},i-1,p,k}))

    利用这个数组优化上面求(f)的过程即可

    注意:虽然从(y)(lca)走求的是最长不上升子序列(与(LIS)相反),但是可以发现(g)数组在(jgeq k)就可以表示最长不上升子序列(虽然这样与定义不符,但确实可以用

    Code

    #include<bits/stdc++.h>
    #define N 30005
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define Min(x,y) ((x)<(y)?(x):(y)) 
    using namespace std;
    const int temp = 16;
    int n,m,dep[N];
    int dp[2][temp][6],g[N][temp][6][6],f[N][temp];
    int l[N],r[N],cl,cr;//倍增时向上经过的断点 
    bool rev;
    
    struct Edge
    {
    	int next,to,dis;
    }edge[N<<1];int head[N],cnt=1;
    void add_edge(int from,int to,int dis)
    {
    	edge[++cnt].next=head[from];
    	edge[cnt].to=to;
    	edge[cnt].dis=dis;
    	head[from]=cnt;
    }
    template <class T>
    void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
    }
    void dfs(int rt,int fa)
    {
    	dep[rt]=dep[fa]+1;
    	for(int i=head[rt];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(v==fa) continue;
    		f[v][0]=rt;
    		for(int j=1;j<=5;++j)
    		for(int k=1;k<=5;++k) 
    		if(Min(j,k)<=edge[i].dis&&Max(j,k)>=edge[i].dis) g[v][0][j][k]=1;
    		for(int j=1;j<temp;++j)
    		{
    			f[v][j]=f[f[v][j-1]][j-1];
    			if(!f[v][j]) break;
    			for(int q=1;q<=5;++q)
    				for(int p=1;p<=5;++p)
    					for(int k=Min(p,q);k<=Max(p,q);++k)
    						g[v][j][q][p]=Max(g[v][j][q][p],g[v][j-1][q][k]+g[f[v][j-1]][j-1][k][p]);//attention 
    		}
    		dfs(v,rt);
    	}
    }
    void init(int x,int y)
    {
    	rev=cl=cr=0;
    	if(dep[x]<dep[y]) {swap(x,y);rev=1;}
    	for(int i=temp-1;i>=0;--i) if(dep[f[x][i]]>=dep[y]) x=f[x][i],l[++cl]=i;
    	if(x==y) return;
    	for(int i=temp-1;i>=0;--i) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i],l[++cl]=i,r[++cr]=i;
    	l[++cl]=0; r[++cr]=0;
    }
    int DP(int x,int y,int cl,int cr,int l[],int r[])
    {
    	for(int i=1;i<=cl;++i)
    	{
    		for(int j=1;j<=5;++j)//之前是j 
    			for(int k=j;k<=5;++k)//现在是k 
    				dp[0][i][k]=Max(dp[0][i][k],dp[0][i-1][j]+g[x][l[i]][j][k]);
    		x=f[x][l[i]];
    	}
    	for(int i=1;i<=cr;++i)//y相反 
    	{
    		for(int j=5;j>=1;--j)//之前是j
    			for(int k=j;k>=1;--k)//现在是k
    				dp[1][i][k]=Max(dp[1][i][k],dp[1][i-1][j]+g[y][r[i]][j][k]);
    		y=f[y][r[i]];
    	}
    	int ans=0;
    	for(int i=1;i<=5;++i)
    		for(int j=i;j<=5;++j)
    			ans=Max(ans,dp[0][cl][i]+dp[1][cr][j]);
    	return ans;
    }
    int main()
    {
    	read(n);
    	for(int i=1;i<n;++i)
    	{
    		int x,y,z;
    		read(x);read(y);read(z);
    		add_edge(x,y,z);
    		add_edge(y,x,z);
    	}
    	dfs(1,0);
    	
    	read(m);
    	while(m--)
    	{
    		memset(dp,0,sizeof(dp));
    		int x,y;
    		read(x);read(y);
    		init(x,y);
    		printf("%d
    ",rev ? DP(x,y,cr,cl,r,l) : DP(x,y,cl,cr,l,r));
    	}
    	return 0;
    }
    
  • 相关阅读:
    .net通用签名方法 webapi签名方法
    实体类的[Serializable]标签造成WebAPI Post接收不到值
    html5获取位置信息,h5获取位置信息
    C#采集:图灵机器人信息
    virtualbox压缩虚拟机硬盘文件vhd
    WinFrom控件双向绑定
    ILMerge合并多个DLL
    在Windows Server 2012 R2的Hyper-V中设置虚拟机启用增强会话模式
    (转)✈工欲善其事,必先利其器✔™
    .NET使用ZXing.NET生成中间带图片的二维码
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11603677.html
Copyright © 2011-2022 走看看