POJ3728 LCA RMQ 动态规划

来源:互联网 发布:寻路算法之a star 编辑:程序博客网 时间:2024/06/06 00:43

题意简述:给定一个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\n",solve(a,b));} return 0;}
这个题目似乎是北大月赛的题目,不得不佩服他们题目的质量,渣校只能仰望了。

题意简述: 给定一个有N个节点的树, 1<=N<=50000,每个节点视作一个城市, 给定某个商品在每个城市的价格,即每个城市有一个权值。 
0 0
原创粉丝点击