【JZOJ4587】Snow的追寻

Description

【JZOJ4587】Snow的追寻_第1张图片

Solution

一道很裸的题目,51Nod上有一个加强版的叫树中最远点对。

思路

有对子树限制的就想一想dfs序。然后在dfs序上建一个线段树每个点存储它表示的点集的最长距离和对应的两个点,合并的时候有六种情况,两个子树的两个点两两配对,然后再与这两个点的值比较,一共六种。
最后查询的时候,限制的两棵子树在dfs序上,分成了三个区间,这三个区间分别询问就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007;
int first[maxn*2],next[maxn*2],last[maxn*2],num;
int i,j,k,l,n,m,q,da,da1,mx,mx1,dfn,wei[maxn],p[maxn],hou[maxn];
int d[maxn],deep[maxn],f[maxn][20];
bool bz[maxn];
struct node{
    int sum,yi,er;
}t[maxn*3],ans;
void add(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void dfs(int x,int y){
    int i;
    wei[x]=++dfn;p[dfn]=x;f[x][0]=y;deep[x]=deep[y]+1;
    rep(i,x){
        if(last[i]!=y){
            dfs(last[i],x);
        }
    }
    hou[x]=dfn;
}
int lca(int x,int y){
    int i,z=0;if(deep[x]y])swap(x,y);
    fod(i,19,0)if(deep[f[x][i]]>deep[y])x=f[x][i];
    if(deep[x]>deep[y])x=f[x][0];
    fod(i,19,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    if(x!=y)return f[x][0];return x;
}
int ju(int x,int y){
    if(!x||!y)return 0;
    int z=lca(x,y);
    return deep[x]+deep[y]-2*deep[z];
}
void merge(node &x,node y,node z){
    int a,b,c,d,e,f;
    a=ju(y.yi,z.yi);b=ju(y.yi,z.er);c=ju(y.er,z.yi);d=ju(y.er,z.er);
    e=max(a,max(b,max(c,d)));
    if(!(e-a))x.yi=y.yi,x.er=z.yi,x.sum=a;if(!(e-b))x.yi=y.yi,x.er=z.er,x.sum=b;
    if(!(e-c))x.yi=y.er,x.er=z.yi,x.sum=c;if(!(e-d))x.yi=y.er,x.er=z.er,x.sum=d;
    if(y.sum>x.sum)x.yi=y.yi,x.er=y.er,x.sum=y.sum;
    if(z.sum>x.sum)x.yi=z.yi,x.er=z.er,x.sum=z.sum;
    if(!x.sum)x.yi=x.er=0;
}
void build(int x,int l,int r){
    if(l==r){
        t[x].yi=t[x].er=p[l];return;
    }
    int mid=(l+r)/2;
    build(x*2,l,mid);build(x*2+1,mid+1,r);
    merge(t[x],t[x*2],t[x*2+1]);
}
void find(int x,int l,int r,int y,int z){
    if(y>z)return;
    if(l==y&&r==z){
        merge(ans,ans,t[x]);return;
    }
    int mid=(l+r)/2;
    if(z<=mid)find(x*2,l,mid,y,z);
    else if(y>mid)find(x*2+1,mid+1,r,y,z);
    else{
        find(x*2,l,mid,y,mid);
        find(x*2+1,mid+1,r,mid+1,z);
    }
}
int main(){
    freopen("snow.in","r",stdin);
    freopen("snow.out","w",stdout);
    scanf("%d%d",&n,&q);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);
        add(k,l);add(l,k);
    }
    dfs(1,0);
    fo(j,1,19){
        fo(i,1,n)f[i][j]=f[f[i][j-1]][j-1];
    }
    build(1,1,n);
    for(;q;q--){
        scanf("%d%d",&k,&l);
        if(k==1||l==1){
            printf("0\n");
            continue;
        }
        if(wei[k]>wei[l])swap(k,l);
        ans.sum=ans.yi=ans.er=0;
        find(1,1,n,1,wei[k]-1);
        find(1,1,n,hou[k]+1,wei[l]-1);
        if(hou[k]>=hou[l])find(1,1,n,hou[k]+1,n);
        else find(1,1,n,hou[l]+1,n);
        printf("%d\n",ans.sum);
    }
}

你可能感兴趣的:(线段树)