JZOJ 3824【NOIP2014模拟9.9】渴

48 篇文章 0 订阅
10 篇文章 0 订阅

Description

世界干涸,Zyh认为这个世界的人们离不开水,于是身为神的他要将他掌控的仅仅两个水源地放置在某两个不同的城市。这个世界的城市因为荒芜,他们仅仅保留了必要的道路,也就是说对于任意两个城市有且仅有一条可行的道路。更简单的,城市形成了一棵树。
Zyh要将这两个水源放在两个不同的城市。饥渴的人们会选择一个离他们最近的水源,并向其走去。每个城市的人的速度都是相同的,并且两个相邻(有边直接相连)的城市的距离都是1,每个人花费的时间也都是1。zyh想知道对于他的每种放置方案,最迟到达的时间是多少。

Solution

这道题比赛的时候打了半天,最后打挂了。
其实思想很简单,细节比较多。
首先所有的询问都要求的点的路径肯定经过树的直径,这个很容易证明。
那么我把树的直径的一段变成树的根节点。
设两个点x,y向上走到第一个树的直径上的节点分别为a和b,假设a的深度较浅。
然后就有三种情况:
1、距a较近的点向上走到树的直径然后再往上走再折一下
2、x和y走到a和b之间,然后再折
3、距b较近的点向上走到树的直径然后再往上走再折一下
折的那个设一个dmax[i]表示i节点不经过树的直径向下最多能走多远
情况1和3都很简单,情况2用数据结构维护一下就好了:
把树的直径放入一个数组里面,然后用线段树维护:序号+dmax和倒序号+dmax
那么再询问a到b的时候,中间的距a的距离然后折:正序号的减去a的序号的值,b的类似。
其实情况2还要分类讨论一下:
先用倍增求出x和y的中点mid,那么mid到a的点都走向y,mid到b的点都走向x(a是y走上去的,b是x走上去的)
1、然后如果mid在a、b之间的话,那么把a到b按mid拆成两段,然后用线段树查询
2、mid在y到a之间,直接用线段树a到b的值,然后加上b到x的距离
2、mid在x到b之间,直接用线段树a到b的值,然后加上a到y的距离
细节比较多

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#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=2e5+7;
int i,j,k,l,n,m,ans,x,y,o,da,root,di,p,yi,er,z,mid,chang;
int first[maxn*2],last[maxn*2],next[maxn*2],num,deep[maxn*2],dao[maxn];
int f[maxn][22],g[maxn],dmax[maxn],qian,a[maxn],h[maxn],son[maxn],shen[maxn];
int zuo[maxn],you[maxn];
int c[maxn];
bool bz[maxn],pp[maxn];
struct node{
    int mx1,mx2;
}t[maxn*4];
void add(int x,int y){last[++num]=y,next[num]=first[x],first[x]=num;}
void dfs(int x,int y){
    int i;
    deep[x]=deep[y]+1,f[x][0]=y;
    rep(i,x){
        if(last[i]!=y){
            dfs(last[i],x);
        }
    }
}
int lca(int x,int y){
    int i;if(deep[x]<deep[y])swap(x,y);
    fod(i,21,0)if(deep[f[x][i]]>deep[y])x=f[x][i];
    if(deep[x]!=deep[y])x=f[x][0];
    fod(i,21,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 dist(int x,int y){return deep[x]+deep[y]-2*deep[lca(x,y)];}
void dfs1(int x,int y){
    int i;
    dmax[x]=deep[x];
    rep(i,x){
        if(last[i]!=y){
            dfs1(last[i],x);
            if(!bz[last[i]]){
                dmax[x]=max(dmax[last[i]],dmax[x]);
            }
        }
    }
}
void build(int x,int l,int r){
    int mid=(l+r)/2;
    if(l==r){
        t[x].mx1=g[l],t[x].mx2=h[l];
        return;
    }
    build(x*2,l,mid);build(x*2+1,mid+1,r);
    t[x].mx1=max(t[x*2].mx1,t[x*2+1].mx1);
    t[x].mx2=max(t[x*2].mx2,t[x*2+1].mx2);
}
int find(int x,int l,int r,int y,int z,int o){
    if(y>z)return 0;
    if(l==y&&r==z){
        if(o)return t[x].mx2;else return t[x].mx1;
    }
    int mid=(l+r)/2;
    if(z<=mid)return find(x*2,l,mid,y,z,o);
    else if(y>mid)return find(x*2+1,mid+1,r,y,z,o);
    else return max(find(x*2,l,mid,y,mid,o),find(x*2+1,mid+1,r,mid+1,z,o));
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        scanf("%d%d",&k,&l);
        add(k,l),add(l,k);
        c[k]++,c[l]++;
    }
    int op=0;
    fo(i,1,n)if(c[i]==1)op++;
    if(op==2){
        fo(i,1,n)if(c[i]==1)op=1;
        deep[op]=1;x=op;l=0;
        while(x){
            pp[x]=1;l++;
            bool lp=1;
            rep(j,x){
                if(!pp[last[j]]){
                    x=last[j];
                    lp=0;
                    break;
                }
            }
            if(lp)break;
            deep[x]=l+1;
        }
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&x,&y);
            if(deep[x]<deep[y])swap(x,y);
            ans=max(deep[y]-1,max(n-deep[x],(deep[x]-deep[y])/2));
            printf("%d\n",ans);
        }
        return 0;
    }
    //--------------------------
    dfs(1,0);
    fo(i,1,n){
        if(deep[i]>da)da=deep[i],root=i;
    }
    dfs(root,0);da=0;
    fo(i,1,n)if(deep[i]>da)da=deep[i],di=i;
    fo(j,1,21)fo(i,1,n)f[i][j]=f[f[i][j-1]][j-1];
    x=di;while(x)bz[x]=1,++a[0],a[da-a[0]+1]=x,x=f[x][0];
    dfs1(root,0);
    fo(i,1,n)dmax[i]=dmax[i]-deep[i];
    //---------------------------------
    qian=l=0;
    fo(i,1,a[0]){
        shen[a[i]]=i;
        g[i]=i+dmax[a[i]];
        zuo[i]=l;
        l=max(l+1,dmax[a[i]]);
    }
    qian=l=0;
    fod(i,a[0],1){
        h[i]=a[0]-i+1+dmax[a[i]];
        dao[a[i]]=a[0]-i+1;
        you[i]=l;
        l=max(l+1,dmax[a[i]]);
    }
    build(1,1,a[0]);
    scanf("%d",&m);int u=0;
    while(m--){
        u++;
        if(u==698){
            ans=ans;
        }
        scanf("%d%d",&x,&y);ans=0;
        o=lca(x,y);yi=lca(di,x),er=lca(di,y);
        if(deep[yi]<deep[er])swap(x,y),swap(yi,er);
        chang=deep[x]-dist(x,y)/2;
        mid=x;fod(i,21,0)if(deep[f[mid][i]]>chang)mid=f[mid][i];
        if(deep[mid]!=chang)mid=f[mid][0];
        if(yi!=er){if(bz[mid])ans=max(find(1,1,a[0],shen[er],shen[mid]-1,0)-shen[er]+dist(y,er),
        find(1,1,a[0],shen[mid],shen[yi],1)-dao[yi]+dist(x,yi));
        else if(dist(y,er)>=chang)ans=max(ans,find(1,1,a[0],shen[er],shen[yi],1)-dao[yi]+dist(x,yi));
        else ans=max(ans,find(1,1,a[0],shen[er],shen[yi],0)-shen[er]+dist(y,er));
    }
        if(!bz[o]||yi==er)ans=max(ans,min(dist(x,er),dist(y,er))+max(you[shen[yi]+1]+1,zuo[shen[er]-1]+1));
        if(dist(y,er)<=dist(x,er))ans=max(ans,dist(y,er)+zuo[shen[er]-1]+1);
        else ans=max(ans,dist(x,er)+zuo[shen[er]-1]+1);
        if(dist(x,yi)<=dist(y,yi))ans=max(ans,dist(x,yi)+you[shen[yi]+1]+1);
        else ans=max(ans,dist(y,yi)+you[shen[yi]+1]+1);
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值