码迷,mamicode.com
首页 > 其他好文 > 详细

BSOJ 4490 避难向导

时间:2019-08-08 21:26:09      阅读:85      评论:0      收藏:0      [点我收藏+]

标签:--   color   二进制   倍增lca   int   为我   putc   通过   dfs   

技术图片技术图片技术图片

题目大意:树上的每一个节点都有一个d[i],定义为离最远节点的距离,还有一个s[i]=(d[i]+a)×b%c,再m次询问,每次询问给定(x,y,q),要求求出(x,y)路径上距x最近的一个点,且满足当前点的s[i]≥q。

emm...这一看就是两道题强行拼起来的,先求出s[i],然后在处理路径上的询问。

显然对于任意点,距离它最远的点一定是直径的两个端点之一,可以用两次DFS把直径的两端求出来,再把两个距离取个max就行了,算完d[i]后就可以算出s[i]啦。

一看到路径上的询问,我就想起了树剖,但冷静后再想想,这是一道静态问题!直接树上倍增乱搞就行了。

我的思路是这样的:

p[i][j]表示i的第2k个祖先,g[i][j]表示i到它的第2k个祖先中s[i]的最大值(不包含i本身)

对于任意一条路径,都可以从LCA(x,y)中拆开(如图)

技术图片

分为两段(x,LCA(x,y)),(LCA(x,y),y)讨论

在(x,LCA(x,y))上时,设Ask(x,k,q)为x到2k祖先中第一个s[i]≥q的i(不含x),通过二分的思想:

Ask(x,k,q)=-1  (g[i][k]<q)

Ask(x,k,q)=p[x][0]  (g[i][0]≥q && k=0)

Ask(x,k,q)=Ask(x,k-1,q) 

Ask(x,k,q)=Ask(p[x][k-1],k-1,q)  (Ask(x,k-1,q)=-1)

有了Ask函数之后,再用二进制拆分(x,LCA(x,y))这条链,若找到第一个答案则直接输出,因为在从x往上爬的途中遇到的第一个答案一定是最近的。

 

在(LCA(x,y),y)上时,同样地,设Ask(x,k,q)为x到2k祖先中第一个s[i]≥q的i(不含x):

Ask(x,k,q)=-1  (g[i][k]<q)

Ask(x,k,q)=p[x][0]  (g[i][0]≥q && k=0)

Ask(x,k,q)=Ask(p[x][k-1],k-1,q)

Ask(x,k,q)=Ask(x,k-1,q)  (Ask(p[x][k-1],k-1,q)=-1)

为什么我们的顺序颠倒了,因为在从y往上爬的途中,答案一定是越靠近LCA(x,y)越优的。而且找到第一个答案不能直接输出,因为我们并不知道后面的区间中是否存在答案,如果有的话当前答案完全可以被替代。

最后的询问区间应该长这样

技术图片

注意:因为g[i][j]是不含i的,所以要在一头一尾加上x和y的特判。

 

#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define int long long
inline int read() {
    char ch;
    bool bj=0;
    while(!isdigit(ch=getchar()))
        bj|=(ch==-);
    int res=ch^(3<<4);
    while(isdigit(ch=getchar()))
        res=(res<<1)+(res<<3)+(ch^(3<<4));
    return bj?-res:res;
}
void printnum(int x) {
    if(x>9)printnum(x/10);
    putchar(x%10+0);
}
inline void print(int x,char ch) {
    if(x<0) {
        putchar(-);
        x=-x;
    }
    printnum(x);
    putchar(ch);
}
const int MAXN=1e5+5;
int n,m,A,B,C,p[MAXN][30],cnt,h[MAXN],g[MAXN][30],log_2[MAXN],dep[MAXN];
int d1[MAXN],d2[MAXN],s[MAXN];
struct Edge {
    int to,nxt,v;
} w[MAXN<<1];
inline void AddEdge(int x,int y,int z) {
    w[++cnt].to=y;
    w[cnt].nxt=h[x];
    w[cnt].v=z;
    h[x]=cnt;
}
void DFS(int x,int fa,int d[],int depth) {
    d[x]=depth;
    for(int i=h[x]; i; i=w[i].nxt) {
        int v=w[i].to;
        if(v==fa)continue;
        DFS(v,x,d,depth+w[i].v);
    }
}
void DFS1(int x,int fa,int depth) {
    p[x][0]=fa;
    g[x][0]=s[fa];
    dep[x]=depth;
    for(int i=h[x]; i; i=w[i].nxt) {
        int v=w[i].to;
        if(v==fa)continue;
        DFS1(v,x,depth+1);
    }
}
inline void Init() {//预处理出d[i]和s[i] 
    int st,ed,maxn=-INF;
    DFS(1,0,d1,0);//从任意节点开始DFS 
    for(int i=1; i<=n; i++)
        if(maxn<d1[i]) {
            maxn=d1[i];
            st=i;//找到直径一端 
        }
    DFS(st,0,d1,0);//从一端DFS
    maxn=-INF;
    for(int i=1; i<=n; i++)
        if(maxn<d1[i]) {
            maxn=d1[i];
            ed=i;//找到直径另一端
        }
    DFS(ed,0,d2,0);//从另一端DFS
    for(int i=1; i<=n; i++) {
        d1[i]=max(d1[i],d2[i]);//取max 
        s[i]=(d1[i]+A)*B%C;
    }
}
inline void ST() {//倍增预处理 
    for(int j=1; j<=log_2[n]; j++)
        for(int i=1; i<=n; i++)
            if(p[i][j-1]) {
                p[i][j]=p[p[i][j-1]][j-1];
                g[i][j]=max(g[i][j-1],g[p[i][j-1]][j-1]);
            }
}
inline int LCA(int x,int y) {//倍增LCA 
    if(dep[x]<dep[y])swap(x,y);
    for(int i=log_2[dep[x]]; ~i; i--)
        if(dep[x]-(1<<i)>=dep[y])x=p[x][i];
    if(x==y)return x;
    for(int i=log_2[dep[x]]; ~i; i--)
        if(p[x][i]&&p[y][i]&&p[x][i]!=p[y][i]) {
            x=p[x][i];
            y=p[y][i];
        }
    return p[x][0];
}
inline int Askx(int x,int k,int q) {//左链的Ask函数,与定义一样 
    if(!k)return g[x][k]>=q?p[x][0]:-1;
    if(g[x][k]>=q) {
        int tmp=Askx(x,k-1,q);
        return tmp!=-1?tmp:Askx(p[x][k-1],k-1,q);
    }
    return -1;
}
inline int Asky(int x,int k,int q) {//同上 
    if(!k)return g[x][k]>=q?p[x][0]:-1;
    if(g[x][k]>=q) {
        int tmp=Asky(p[x][k-1],k-1,q);
        return tmp!=-1?tmp:Asky(x,k-1,q);
    }
    return -1;
}
signed main() {
    n=read();
    m=read();
    A=read();
    B=read();
    C=read();
    for(int i=2; i<=n; i++)log_2[i]=log_2[i>>1]+1;
    int x,y,z;
    for(int i=1; i<n; i++) {
        x=read();
        y=read();
        z=read();
        AddEdge(x,y,z);
        AddEdge(y,x,z);
    }
    Init();
    DFS1(1,0,1);
    ST();
    while(m--) {
        x=read();
        y=read();
        z=read();
        if(s[x]>=z) {//特判x 
            print(x,\n);
            continue;
        }
        int u=LCA(x,y),ans=-1,tmpy=y;
        for(int i=log_2[dep[x]]; ~i; i--)
            if(dep[x]-(1<<i)>=dep[u]) {//二进制拆分 
                ans=Askx(x,i,z);
                if(ans!=-1) {//有答案直接输出 
                    print(ans,\n);
                    break;
                }
                x=p[x][i];
            }
        if(ans!=-1)continue;
        for(int i=log_2[dep[y]]; ~i; i--)
            if(dep[y]-(1<<i)>=dep[u]) {//二进制拆分 
                int r=Asky(y,i,z);
                if(ans==-1||(r!=-1&&dep[r]<dep[ans]))ans=r;//有答案不能直接输出,换成更新当前答案 
                y=p[y][i];
            }
        if(ans==-1)print(s[tmpy]>=z?tmpy:-1,\n);
        else print(ans,\n);
    }
    return 0;
}

 

时间复杂度:预处理O(nlog n)+单次询问O(log2n)

 

BSOJ 4490 避难向导

标签:--   color   二进制   倍增lca   int   为我   putc   通过   dfs   

原文地址:https://www.cnblogs.com/soledadstar/p/11323646.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!