【树DP+LCA】[CQBZOJ2937]避难向导

题目描述
“特大新闻,特大新闻!全国爆发了一种极其可怕的病毒,已经开始在各个城市 中传播开来!全国陷入了巨大的危机!大量居民陷入恐慌,想要逃到其它城市以 避难!经调查显示,该病毒来自于C 市的A 学校的一次非法的……” “哎。”你关上电视,叹了口气。作为A 学校的校长,你一天前为了保命,独自 逃离了A 学校,抛弃了全校师生,包括那个曾经帮你计算并拆除道路的工程师。 你良心受到了巨大的谴责,因此决定做出一些补救,回答一些逃难的人提出的询 问。 已知该国一共有n 个城市,并且1 号城市是首都。(n-1)条双向的公路连接这些 城市,通过这些公路,任意两个城市之间存在且仅存在一条路径。每条公路有一 个长度。如果一个城市只与一条公路相连,则称它为边境城市。 该国政府有一个奇怪的规定:每个城市有一个封闭系数di,定义di 为离这个城 市最远的边境城市到这个城市的距离。市民们认为,一个城市的安全系数Si 和 它的封闭系数有很重要的联系。a,b,c 是该国的幸运数字,所以大家公认一个 城市的安全系数Si = (di + a) * b mod c。 市民们一共会提出m 次询问。每个询问包含三个信息,xi,yi 和qi。xi 是询问 者所在的城市编号。你要为这个询问者在xi 到yi 的必经之路上找出一个离xi 最近的避难城市,并且要求这个避难城市的安全系数大于等于qi。如果存在这 样的城市(包含xi 和yi),则输出城市编号,否则输出一行包括一个数-1。

输入
第一行五个数:依次是n, m, a, b, c。 接下来n-1 行描述公路的信息。每行三个数,前两个数代表这条公路连接的两个 城市的编号,第三个数表示这条公路的长度。 再接下来m 行,每行描述一个询问,包含三个数xi, yi 和qi。

输出
对于每个询问,输出一行包含一个整数,存在符合要求的城市则输出城市编号, 不存在则输出-1。

样例输入
Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

7 6 5 6 20
1 2 4
2 4 2
2 5 3
1 3 5
3 6 6
6 7 7
7 5 15
3 4 5
5 4 2
4 5 2
6 6 10
3 5 19
样例输出
6
3
2
4
6
-1

这里写图片描述
这里写图片描述

分析:
用树DP预处理出离每个城市最远的边境城市,然后以任意一个城市为根进行LCA的预处理,p,mx为倍增数组,分别表示祖先的编号和到祖先路径上安全系数最大的点的安全系数。
对于每次询问,找到x,y,的LCA,然后找x到LCA路径上安全系数>q且离x最近的点,否则,找x到LCA路径上安全系数>q且离x最远的点,每次查询的时间为O(log(n)),总时间复杂度O(n+mlog(n))。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100000
#define MAXLOG 18
int Log,n,m,l[MAXN+10],f[MAXN+10][2],ms[MAXN+10],t,a,b,c,x,y,qi,g[MAXN+10],s[MAXN+10],ans,fa[MAXN+10],p[MAXN+10][MAXLOG],mx[MAXN+10][MAXLOG];
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
struct node{
    int v,wt;
    node *next;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=edge;
void addedge(int u,int v,int wt){
    node *p=++ecnt;
    p->v=v;
    p->wt=wt;
    p->next=adj[u];
    adj[u]=p;
}
void dfs1(int u){
    int v;
    for(node *p=adj[u];p;p=p->next){
        v=p->v;
        if(v==fa[u])
            continue;
        fa[v]=u;
        l[v]=l[u]+1;
        dfs1(v);
        if(f[v][0]+p->wt>f[u][0]){
            f[u][1]=f[u][0];
            f[u][0]=f[v][0]+p->wt;
            ms[u]=v;
        }
        else if(f[v][0]+p->wt>f[u][1])
            f[u][1]=f[v][0]+p->wt;
    }
}
void dfs2(int u,int wt){
    if(ms[fa[u]]==u)
        g[u]=f[fa[u]][1];
    else
        g[u]=f[fa[u]][0];
    g[u]=max(g[u],g[fa[u]])+wt;
    for(node *p=adj[u];p;p=p->next)
        if(p->v!=fa[u])
            dfs2(p->v,p->wt);
}
void prepare(){
    int i,j;
    for(i=1;i<=n;i++){
        p[i][0]=fa[i];
        mx[i][0]=s[fa[i]];
    }
    for(j=1;j<=Log;j++)
        for(i=1;i<=n;i++){
            p[i][j]=p[p[i][j-1]][j-1];
            mx[i][j]=max(mx[i][j-1],mx[p[i][j-1]][j-1]);
        }
}
int lca(int x,int y){
    int i;
    if(l[x]<l[y])
        swap(x,y);
    for(i=Log;i>=0;i--)
        if(l[x]-(1<<i)>=l[y])
            x=p[x][i];
    if(x==y)
        return x;
    for(i=Log;i>=0;i--)
        if(p[x][i]!=p[y][i])
            x=p[x][i],y=p[y][i];
    return fa[x];
}
int find1(int x,int d,int a){
    if(l[x]<=l[a])
        return -1;
    for(;l[p[x][d]]<l[a]&&d>=0;d--);
    if(mx[x][d]<qi)
        return find1(p[x][d],d-1,a);
    for(int i=d-1;i>=0;i--)
        if(mx[x][i]<qi)
            x=p[x][i];
    return fa[x];
}
int find2(int x,int d,int a){
    int i,ret;
    if(l[x]<=l[a])
        return -1;
    for(;l[p[x][d]]<l[a]&&d>=0;d--);
    ret=find2(p[x][d],d-1,a);
    if(~ret)
        return ret;
    if(mx[x][d]<qi)
        return -1;
    for(i=d-1;i>=0;i--)
        if(mx[p[x][i]][i]>=qi)
            x=p[x][i];
    return fa[x];
}
int main()
{
    int i,u,v,wt;
    Read(n),Read(m),Read(a),Read(b),Read(c);
    for(Log=0;1<<Log<=n;Log++);
    Log--;
    for(i=1;i<n;i++){
        Read(u),Read(v),Read(wt);
        addedge(u,v,wt);
        addedge(v,u,wt);
    }
    dfs1(1);
    dfs2(1,0);
    for(i=1;i<=n;i++)
        s[i]=((long long)max(f[i][0],g[i])+a)*b%c;
    prepare();
    while(m--){
        Read(x),Read(y),Read(qi);
        int d,a=lca(x,y);
        ans=-1;
        if(s[x]>=qi)
            ans=x;
        if(ans==-1){
            for(d=0;1<<d<=l[x];d++);
            d--;
            ans=find1(x,d,a);
        }
        if(ans==-1){
            for(d=0;1<<d<=l[y];d++);
            d--;
            ans=find2(y,d,a);
        }
        if(ans==-1&&s[y]>=qi)
            ans=y;
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值