zoukankan      html  css  js  c++  java
  • poj3728 The merchant[倍增]

    给一棵点带权树,$q$次询问,问树上$x$到$y$路径上,两点权之差(后面的减去前面的)的最大值。


    这个是在树链上找点,如果沿路径的最小值在最大值之前出现那肯定答案就是$maxx-minx$,但是反之就不好办了。。

    方法一:在线倍增合并答案

    先来看一个退化成链的情况:区间$ql,qr$内找$i<j$使$A_j-A_i$值最大怎么做。

    这里尝试线段树解决。假设两个小区间合并答案的话,维护一个$dif_i$表示区间$i$上述答案。

    那么合并区间答案时,要么答案出自左半区间,要么右半区间,要么跨中间,一个取左边的另一个取右边的,贪心可知取左半区间min和右半区间max。

    这个其实就是一个对不同情况答案的分类讨论,看来我分类来取最佳答案的思想还不够啊,区间最大连续和不也是同一类型的么。

    然后由启发,放到树上,于是一条树链就可以通过lca左边、lca右边、lca两边两条链的min和max来求得答案。

    维护树链上信息可以有树剖等,但是这里发现使用倍增维护答案时,向上跳lca时的各小链的答案是具有合并性的,树剖的话就繁掉了。

    于是倍增维护$upw[i][k],dow[i][k]$,表示从$i$向上跳$2^k$步的答案,以及从上面的$2^k$距离处跳下来的答案。

    跳的时候每跳一段,为了和之前已经跳的一大段合并答案,取之前一大段的min和当前这一段的max作差比较更新,思路其实和线段树差不多。

    因为我比较懒,所以没太想多少,写的常数比较大。

    复杂度是$O(nlogn)$的。

    【注意思路】分类讨论总结答案!根据维护信息的性质选择维护方法!

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #define dbg(x) cerr << #x << " = " << x <<endl
     7 using namespace std;
     8 typedef long long ll;
     9 typedef double db;
    10 typedef pair<int,int> pii;
    11 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    12 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    13 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    14 template<typename T>inline T _max(T A,T B,T C){return _max(_max(A,B),C);}
    15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    17 template<typename T>inline T read(T&x){
    18     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    19     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    20 }
    21 const int N=50000+7,INF=0x3f3f3f3f;
    22 int n,q,qx,qy,_pow[N];
    23 //edge
    24 int Head[N],A[N],tot;
    25 struct thxorz{int to,nxt;}G[N<<1];
    26 inline void Addedge(int x,int y){
    27     G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot;
    28     G[++tot].to=x,G[tot].nxt=Head[y],Head[y]=tot;
    29 }
    30 //lca
    31 int f[N][18],minv[N][18],maxv[N][18],upw[N][18],dow[N][18],depth[N];
    32 #define y G[j].to
    33 void dfs(int x,int fa){
    34     f[x][0]=fa,minv[x][0]=maxv[x][0]=A[x],upw[x][0]=dow[x][0]=0;
    35     for(register int i=1;i<=_pow[depth[x]];++i){
    36         f[x][i]=f[f[x][i-1]][i-1];
    37         minv[x][i]=_min(minv[x][i-1],minv[f[x][i-1]][i-1]);
    38         maxv[x][i]=_max(maxv[x][i-1],maxv[f[x][i-1]][i-1]);
    39         upw[x][i]=_max(upw[x][i-1],upw[f[x][i-1]][i-1],maxv[f[x][i-1]][i-1]-minv[x][i-1]);
    40         dow[x][i]=_max(dow[x][i-1],dow[f[x][i-1]][i-1],maxv[x][i-1]-minv[f[x][i-1]][i-1]);
    41     }
    42     for(register int j=Head[x];j;j=G[j].nxt)if(y^fa)depth[y]=depth[x]+1,dfs(y,x);
    43 }
    44 #undef y
    45 inline int LCA(int x,int y){
    46     if(depth[x]<depth[y])_swap(x,y);
    47     for(register int i=_pow[depth[x]-depth[y]];~i;--i)
    48         if(depth[f[x][i]]>=depth[y])
    49             x=f[x][i];
    50     if(x==y)return y;
    51     for(register int i=_pow[depth[x]];~i;--i)
    52         if(f[x][i]^f[y][i])
    53             x=f[x][i],y=f[y][i];
    54     return f[x][0];
    55 }
    56 inline int Query(int x,int y){
    57     int lca=LCA(x,y),ans=0,minx=INF,maxx=0;//dbg(lca);
    58     for(register int i=_pow[depth[x]-depth[lca]];~i;--i)
    59         if(depth[f[x][i]]>=depth[lca])
    60             MAX(ans,upw[x][i]),MAX(ans,maxv[x][i]-minx),MIN(minx,minv[x][i]),x=f[x][i];
    61     MIN(minx,A[lca]);
    62     for(register int i=_pow[depth[y]-depth[lca]];~i;--i)
    63         if(depth[f[y][i]]>=depth[lca])
    64             MAX(ans,dow[y][i]),MAX(ans,maxx-minv[y][i]),MAX(maxx,maxv[y][i]),y=f[y][i];
    65     MAX(maxx,A[lca]);
    66     return _max(ans,maxx-minx);
    67 }
    68 
    69 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
    70     for(register int i=1;i<=5e4;++i)_pow[i]=__lg(i);
    71     read(n);for(register int i=1;i<=n;++i)read(A[i]);
    72     for(register int i=1;i<n;++i)read(qx),read(qy),Addedge(qx,qy);
    73     depth[1]=1,dfs(1,0);read(q);
    74     while(q--)read(qx),read(qy),printf("%d
    ",Query(qx,qy));
    75     return 0;
    76 }

    方法二:tarjan+带权并查集离线处理询问(☆)

    由于之前没学过tarjan离线LCA,于是把这个东西学了一下。STO tarjan ORZ。具体的学习地址放在了转载内容中了。还是很好理解的。

    然后看这题,让我对tarjan算法有了进一步的认识,也就是基于并查集的改造维护一些信息。

    首先基本思路还是一样的。是把答案拆成lca左边链的答案和右边链的答案加上跨过lca的答案取max,然后求点到lca链的信息(upw,dow,max,min)不再用倍增处理了。

    采用lca自底向上逐步合并答案的方法。

    由于信息的可合并性,考虑用离线LCA,同时并查集带权,维护上述信息,该点到此时的该点ancestor一条长链的信息就可以查询并查集并在回去时维护好即可。

    注意到一个点作为LCA的时候,其询问的点对一定在子树中,等我把子树全dfs好了(dfs时把lca记下来,详见code),最后再处理以子树根为LCA的点对答案,将询问点对在并查集中路径压缩一下,合并信息。

    然后就离线处理了答案。详见code。个人觉得还蛮清楚的。

    复杂度$O(n+q alpha (n))$,忽略反阿克曼函数,量级就是$O(n)$的。快了许多。


    由于写这题的时候环境较吵,写了许多错误,调了半小时,也表明功底不扎实啊。

    记录一下错误:

    • line41:注意到并查集压缩路径时,ancestor是先变掉的,所以更新信息时要保存父亲,递归完更新。
    • line46:打错minv,醉了。。
    • line43~46:注意并查集合并、更新信息的顺序,不能先更新minv、maxv,不然会把新的minv、maxv覆盖在upw、dow上面。
    • line55:注意得到正确的询问顺序以及询问序号,并且line56记得再维护一下点对信息再算答案。
    • line59:别漏了vis。。。

    总之这种做法还是很重要的。两点启示:1.维护lca链的相关可合并信息,可以用离线处理lca带权并查集维护。2.多个维护信息要注意更新顺序。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #define dbg(x) cerr << #x << " = " << x <<endl
     7 using namespace std;
     8 typedef long long ll;
     9 typedef double db;
    10 typedef pair<int,int> pii;
    11 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    12 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    13 template<typename T>inline T _max(T A,T B,T C){return _max(_max(A,B),C);}
    14 template<typename T>inline T _min(T A,T B,T C){return _min(_min(A,B),C);}
    15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    18 template<typename T>inline T read(T&x){
    19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    21 }
    22 const int N=5e4+7;
    23 int n,m,ans[N];
    24 struct thxorz{int to,nxt;}G[N<<1],Q[N<<1],lca[N];
    25 int Head[N],Qhead[N],lhead[N],tot=1;
    26 inline void Addedge(int x,int y){
    27     G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot;
    28     G[++tot].to=x,G[tot].nxt=Head[y],Head[y]=tot;
    29 }
    30 inline void AddQuery(int x,int y){
    31     Q[++tot].to=y,Q[tot].nxt=Qhead[x],Qhead[x]=tot;
    32     Q[++tot].to=x,Q[tot].nxt=Qhead[y],Qhead[y]=tot;
    33 }
    34 inline void Addlca(int x,int y){
    35     lca[++tot].to=y,lca[tot].nxt=lhead[x],lhead[x]=tot;
    36 }
    37 
    38 struct disjoint_set{int maxv,minv,upw,dow,anc;}T[N];
    39 inline int Find_anc(int x){
    40     if(T[x].anc==x)return x;
    41     int tmp=T[x].anc;
    42     T[x].anc=Find_anc(T[x].anc);
    43     T[x].upw=_max(T[x].upw,T[tmp].upw,T[tmp].maxv-T[x].minv);
    44     T[x].dow=_max(T[x].dow,T[tmp].dow,T[x].maxv-T[tmp].minv);
    45     T[x].maxv=_max(T[x].maxv,T[tmp].maxv);
    46     T[x].minv=_min(T[x].minv,T[tmp].minv);//attetion:the order.
    47     return T[x].anc;
    48 }
    49 #define y G[j].to
    50 int vis[N];
    51 void tarjan(int x,int fa){
    52     for(register int j=Head[x];j;j=G[j].nxt)if(y^fa)tarjan(y,x),T[y].anc=x;
    53     for(register int j=Qhead[x];j;j=Q[j].nxt)if(vis[Q[j].to])Addlca(Find_anc(Q[j].to),j>>1);
    54     for(register int j=lhead[x];j;j=lca[j].nxt){
    55         int a=Q[(lca[j].to<<1)^1].to,b=Q[lca[j].to<<1].to;
    56         Find_anc(a),Find_anc(b);
    57         ans[lca[j].to]=_max(T[a].upw,T[b].dow,T[b].maxv-T[a].minv);
    58     }
    59     vis[x]=1;
    60 }
    61 #undef y
    62 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
    63     read(n);
    64     for(register int i=1,x;i<=n;++i)read(x),T[i].anc=i,T[i].minv=T[i].maxv=x,T[i].dow=T[i].upw=0;
    65     for(register int i=1,x,y;i<n;++i)read(x),read(y),Addedge(x,y); 
    66     read(m);tot=1;
    67     for(register int i=1,x,y;i<=m;++i)read(x),read(y),AddQuery(x,y);
    68     tot=1;tarjan(1,0);
    69     for(register int i=1;i<=m;++i)printf("%d
    ",ans[i]);
    70     return 0;
    71 }
  • 相关阅读:
    Kubenetes环境搭建笔记
    Python+Robot Framework实现UDS诊断自动化测试
    Python实现CAN总线J1939报文接收、发送
    [转载]从SQL 2008 复制数据及对像到SQL 2000 的方法
    推荐移动应用:群落(Groupcells)——全球第一款基于图片组的近场社交电子商务平台
    [缓存]迅雷下载原理
    HP大中华区总裁孙振耀退休感言
    [缓存]HTTP协议中的TranferEncoding:chunked编码解析
    [转载]SQL 2008到2005和2000版本的转换
    [学习]SVM入门(一)
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11438629.html
Copyright © 2011-2022 走看看