zoukankan      html  css  js  c++  java
  • POJ3728 THE MERCHANT LCA RMQ DP

    题意简述:给定一个N个节点的树,1<=N<=50000 每个节点都有一个权值,代表商品在这个节点的价格。商人从某个节点a移动到节点b,且只能购买并出售一次商品,问最多可以产生多大的利润。

    算法分析:显然任意两个城市之间的路径是唯一的,商人有方向地从起点移动到终点。询问这条路径上任意两点权值之差最大为多少,且要保证权值较大的节点在路径上位于权值较小的节点之后。 

      暴力的方法是显而易见的,只要找到两个点的深度最深的公共祖先,就等于找到了这条路径,之后沿着路径走一遍即可找到最大的利润,然而无法满足50000的数据规模。

      首先考虑高效寻找LCA(公共祖先)的方法。记录ance[i][j]为节点i向上走2^j步到达的某个祖先。可以简单地列出方程 ance[i][j]=ance[ance[i][j-1]][j-1];于是找到了高效构建的方法。

      每次寻找LCA 首先将两个节点通过swim(a,b)函数转移到同一深度,然后每次找一个最小的j使得ance[a][j]==ance[b][j] 之后将节点a赋值为ance[a][j-1] 直到j=0就找到了两者的LCA

      现在我们已经找到了高效寻找LCA的方法,假设我们知道节点a到LCA的最小值minp[],LCA到节点b的最大值maxp[],

      以及买卖地点全在LCA之前可以获得的最大利润maxi[] 以及买卖地点全在LCA之后可以获得的最大利润maxI[] 显然就得到了最后的答案。 维护这些数据的方式类似于维护ance数组的方式,DP方程也很好列出, 这里就不给出了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<set>
    #include<map>
    #include<stack>
    using namespace std;
    const int maxn=50000+10;
    vector<int>po[maxn];
    int dep[maxn],ance[maxn][17],maxp[maxn][17],minp[maxn][17],maxi[maxn][17],maxI[maxn][17],price[maxn],n,fa[maxn];
    bool vis[maxn];
    int max(int a,int b)
    {
     if(a>b)return a;
     	else return b;
    }
    int min(int a,int b)
    {
     if(a>b)return b;
     	else return a;
    }
    queue<int> q;
    void BFS_build()
    {
     memset(vis,0,sizeof(vis));
     
     q.push(1);
     fa[1]=1;
     dep[1]=1;
     vis[1]=true;
     while(!q.empty())
     	{
     	 int np=q.front();q.pop();
     	 ance[np][0]=fa[np];
     	 maxp[np][0]=max(price[np],price[fa[np]]);
     	 minp[np][0]=min(price[np],price[fa[np]]);
     	 if(price[np]<price[fa[np]])maxi[np][0]=price[fa[np]]-price[np];
     	 	else maxi[np][0]=0;
     	 if(price[np]>price[fa[np]])maxI[np][0]=price[np]-price[fa[np]];
     	 	else maxI[np][0]=0;
     	 for(int i=1;i<=16;i++)//倍增DP方程 
     	    {   
     	  		ance[np][i]=ance[ance[np][i-1]][i-1];
     	  		maxp[np][i]=max(maxp[np][i-1],maxp[ance[np][i-1]][i-1]);
     	  		minp[np][i]=min(minp[np][i-1],minp[ance[np][i-1]][i-1]);
     	  		int a=maxi[np][i-1],b=maxi[ance[np][i-1]][i-1],c=0;
     	  		c=maxp[ance[np][i-1]][i-1]-minp[np][i-1];
     	  		maxi[np][i]=max(max(a,b),c);
     	  	    a=maxI[np][i-1];b=maxI[ance[np][i-1]][i-1];c;
     	  		c=maxp[np][i-1]-minp[ance[np][i-1]][i-1];
     	  		maxI[np][i]=max(max(a,b),c);
     	  		if(ance[np][i]==1)break;
     		}
     	 for(int i=0;i<po[np].size();i++)
     	 	{
     	 	 int nv=po[np][i];
     	 	 if(vis[nv])continue;
     	 	 fa[nv]=np;
     	 	 dep[nv]=dep[np]+1;
     	 	 q.push(nv);
     	 	 vis[nv]=true;
    		}
    	}
    }
    int ia,ib,mi,ma;
    int ancest;
    void swim(int &a,int &b)
    {
     if(dep[a]==dep[b])return ;
     while(dep[a]>dep[b])
     	{
     	 int i;
     	 for(i=0;i<=16;i++)
     	 	{
     	 	 if(pow(2,i)+dep[b]>dep[a])break;
    		}
    	 ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
    	 mi=min(mi,minp[a][i-1]);
    	 a=ance[a][i-1];
    	}
     while(dep[a]<dep[b])
     	{
     	 int i;
     	 for(i=0;i<=16;i++)
     	 	{
     	 	 if(pow(2,i)+dep[a]>dep[b])break;
    		}
    	 ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
    	 ma=max(ma,maxp[b][i-1]);
         b=ance[b][i-1];
    	}
    }
    int solve(int a,int b)
    {
     ia=0;ib=0;mi=price[a];ma=price[b];
     swim(a,b);
     if(a==b)return max(max(ia,ib),ma-mi);
      
     while(true)
     	{
     	 int i;
     	 for(i=0;i<=16;i++)
     	 	{
     	 	 if(ance[a][i]==ance[b][i])break;
    		}
    	 if(i==0)
    	 	{
    	 	 ancest=ance[a][0];
    	 	 ia=max(ia,price[ancest]-mi);
    	 	 ib=max(ib,ma-price[ancest]);
    	 	 mi=min(mi,price[ancest]);
    	 	 ma=max(ma,price[ancest]);
    	 	 return max(max(ia,ib),ma-mi);
    		}
    			else
    				{
    				 ia=max(max(ia,maxi[a][i-1]),maxp[a][i-1]-mi);
    				 ib=max(max(ib,maxI[b][i-1]),ma-minp[b][i-1]);
    				 mi=min(mi,minp[a][i-1]);
    				 ma=max(ma,maxp[b][i-1]);
    				 a=ance[a][i-1];b=ance[b][i-1];
    				}
    	}
    
    }
    int main()
    {freopen("t.txt","r",stdin);
     scanf("%d",&n);
     
     for(int i=1;i<=n;i++)
     	scanf("%d",&price[i]);
     for(int i=1;i<n;i++)
     	{
     	 int a,b;
     	 scanf("%d%d",&a,&b);
     	 po[a].push_back(b);po[b].push_back(a);
    	}
     BFS_build();
     int p;
     scanf("%d",&p);
     for(int i=1;i<=p;i++)
     	{
     	 int a,b;
    	 scanf("%d%d",&a,&b);
    	 printf("%d
    ",solve(a,b));	
    	}
     return 0;
    }
    

      这个题目似乎是北大月赛的题目,不得不佩服他们题目的质量,渣校只能仰望了。

  • 相关阅读:
    JAVA程序的运行机制
    DOS命令
    垃圾回收
    eureka的简单使用
    各微服务之间的调用
    各层调用关系与注解使用
    bean管理xml方式
    Lombok介绍和使用
    java特性 JDK JRE JVM
    git克隆 文件夹
  • 原文地址:https://www.cnblogs.com/heisenberg-/p/5471202.html
Copyright © 2011-2022 走看看