避難嚮導 樹的直徑 樹上倍增 二分答案

NKOJ3489【2015多校聯訓5】避難嚮導

問題描述

你良心受到了巨大的譴責,因此決定做出一些補救,回答一些逃難的人提出的詢問。
已知該國一共有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。

樣例輸入

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

數據範圍

對於100%數據, 0< xi, yi<=n<=100000; a,b,c,qi<=1,000,000;
注意:計算安全係數的中間過程可能需要使用64 位整數。


首先要解決離某個城市最遠的城市的距離。樹形DP當然可以搞,但是這在主要問題之前就給自己增加了難度。實際上,離任意一個城市最遠的點一定是直徑的端點。這個採用反證法不難證明。找樹的直徑不就是用的這個性質嗎?

幾遍DFS就可以方便地求出Si 。現在考慮如何處理詢問。

既然是與路徑相關,肯定要考慮LCA。

首先考慮判無解。求出一段路徑中點權的最值,可以使用倍增法,注意代碼中的V[x][k] 不包含點x 本身,處理好端點就是最值的細節。

在考慮有解的情況下怎樣找到離起點最近且權值滿足要求的點。這樣的點無非有兩種情況:在起點到LCA的路徑上或是在LCA到終點的路徑上。分別考慮如何解決:

在起點到LCA的路徑上:這樣的點要求深度儘量深。借鑑倍增法求LCA的思想即可:從大到小枚舉i ,看向上跳2i 是否滿足要求,不滿足就向上跳。複雜度O(logn)

在LCA到終點的路徑上:這樣的情況相對更難搞,因爲要求的是深度儘可能淺。由於從LCA到終點的路徑上的最值單調不減,可以用二分答案討論改點到終點的距離,向上跳之後倍增驗證即可,複雜度O(log2n)

所以,處理詢問時,先討論是否有解;若有解,則先討論第一種情況,不滿足則討論第二種情況。最壞情況下時間複雜度O(nlog2n) ,可以卡過。

本題涉及知識較多,雖然難度不大,但代碼量比較大,細節的處理比較多。考試時過不了,主要是忘記了這個二分答案模板求最大值輸出R,最小值輸出L。由於是二分的向上跳的距離,所以要求儘可能大,取R纔對。


代碼:

#include<stdio.h>
#include<algorithm>
#define MAXN 300005
#define MAXM 600005
#define ll long long
using namespace std;

int N,M;

int en[MAXM],nex[MAXM],las[MAXN],tot;
ll len[MAXM];

void Add(int x,int y,ll z) {
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=z;
}

int ID,St,En;
ll MaxDis;

void dfsd(int x,int f,ll dis) {
    int i,y;
    if(dis>MaxDis)MaxDis=dis,ID=x;
    for(i=las[x]; i; i=nex[i]) {
        y=en[i];
        if(y==f)continue;
        dfsd(y,x,dis+len[i]);
    }
}

void FindD() {//找直徑的端點
    MaxDis=0;
    dfsd(1,0,0);
    St=ID;
    MaxDis=0;
    dfsd(ID,0,0);
    En=ID;
}

ll S[MAXN],D[MAXN];

void dfss(int x,int f,ll dis) {
    int i,y;

    if(D[x]<dis)D[x]=dis;

    for(i=las[x]; i; i=nex[i]) {
        y=en[i];
        if(y==f)continue;
        dfss(y,x,dis+len[i]);
    }
}

void GetS(ll A,ll B,ll C) {
    dfss(St,0,0);
    dfss(En,0,0);

    int i;
    for(i=1; i<=N; i++)S[i]=(D[i]+A)%C*B%C;
}

int dep[MAXN],fa[MAXN][20];
ll V[MAXN][20];
void DFS(int x,int f) {
    int i,y;

    dep[x]=dep[f]+1;
    fa[x][0]=f;
    V[x][0]=S[f];
    for(i=1; i<20; i++) {
        fa[x][i]=fa[fa[x][i-1]][i-1];
        V[x][i]=max(V[x][i-1],V[fa[x][i-1]][i-1]);
    }

    for(i=las[x]; i; i=nex[i]) {
        y=en[i];
        if(y==f)continue;
        DFS(y,x);
    }
}

ll MaxV;
int LCA(int x,int y) {
    int i,t,d;

    MaxV=max(S[x],S[y]);

    if(dep[x]<dep[y])t=x,x=y,y=t;
    d=dep[x]-dep[y];
    for(i=0; i<20; i++)if((d>>i)&1)MaxV=max(MaxV,V[x][i]),x=fa[x][i];
    if(x==y)return x;
    for(i=19; i>=0; i--)
        if(fa[x][i]!=fa[y][i]) {
            MaxV=max(MaxV,V[x][i]);
            MaxV=max(MaxV,V[y][i]);
            x=fa[x][i];
            y=fa[y][i];
        }
    MaxV=max(MaxV,V[x][0]);
    MaxV=max(MaxV,V[y][0]);
    return fa[x][0];
}

int Up(int x,int lca,ll k) {//情況一
    int i;

    for(i=19; i>=0; i--)if(V[x][i]<k)x=fa[x][i];
    if(dep[fa[x][0]]<dep[lca])return -1;
    return fa[x][0];
}

int Down(int y,int lca,ll k) {//情況二
    int i,d,L,R,mid,t;
    ll tmpv;

    d=dep[y]-dep[lca];
    L=0;
    R=d;
    while(L<=R) {//二分從終點向上跳的距離
        mid=L+R>>1;

        t=y;
        for(i=0; i<20; i++)if((mid>>i)&1)t=fa[t][i];
        tmpv=S[t];
        for(i=0; i<20; i++)if(((d-mid)>>i)&1)tmpv=max(tmpv,V[t][i]),t=fa[t][i];

        if(tmpv>=k)L=mid+1;
        else R=mid-1;

    }
    for(i=0; i<20; i++)if((R>>i)&1)y=fa[y][i];
    return y;
}

int main() {

    int i,x,y,lca,ans;

    ll A,B,C,z;

    scanf("%d%d%lld%lld%lld",&N,&M,&A,&B,&C);

    for(i=1; i<N; i++) {
        scanf("%d%d%lld",&x,&y,&z);
        Add(x,y,z);
        Add(y,x,z);
    }

    FindD();
    GetS(A,B,C);

    DFS(1,0);

    while(M--) {
        scanf("%d%d%lld",&x,&y,&z);
        if(S[x]>=z) {
            printf("%d\n",x);
            continue;
        }

        lca=LCA(x,y);
        if(MaxV<z) {
            puts("-1");
            continue;
        }

        ans=Up(x,lca,z);
        if(ans!=-1)printf("%d\n",ans);
        else ans=Down(y,lca,z),printf("%d\n",ans);
    }
}
發佈了105 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章