[LOJ 6435][PKUSC 2018]星際穿越

[LOJ 6435][PKUSC 2018]星際穿越

題意

給定 \(n\) 個點, 每一個點與 \([l_i,i-1]\) 之間的點創建有單位距離的雙向邊. \(q\) 組詢問從 \(x\) 走到 \([l,r]\) 中的隨機一點的指望距離. 輸出既約分數.c++

\(n,q\le 3\times 10^5\), \(l<r<x\).git

題解

顯然對於一個 \(k\), \(k\) 步以內能到達的點是 \([1,x)\) 的一個後綴. 那麼也就是說 \([1,x)\) 中的點的答案被分紅了若干段, 每段的答案相同且向前遞增.spa

手動模擬一下, 咱們發現第一步能夠走到的左端點是 \(l_x\), 第二步能夠走到的就是 \(\min\limits_{k\ge l_x}l_k\) 了. 緣由是全部可能一步到達 \(\min\limits_{k\ge l_x}l_k\) 的點都必然能夠被 \(x\) 一步到達(若是 \(l_k\)\(k>x\) 處取得最小值, 那麼顯然 \(l_k<x\). 又因爲 \(k\) 鏈接的是 \([l_k,k)\), 那麼必然和 \(x\) 直接相連). 且後面的步驟都是形如 \(l_k\) 的後綴 \(\min\) 的形式.3d

因而咱們能夠在第二步及之後嘗試倍增.code

倍增的同時不只記錄到達的左端點, 同時記錄一下倍增時跳過的點的距離總和. 這樣就能夠在 \(O(\log n)\) 的時間內計算出 \(x\)\([l,x)\) 內的全部點的最短路之和了. 兩個後綴和相減便可獲得 \([l,r]\) 內的答案.blog

最後約分一下輸出就完了. 雖然最終答案是 \(O(n^2)\) 級別的可是數據比較弱並無爆 int...get

參考代碼

#include <bits/stdc++.h>

const int MAXN=3e5+10;

int n;
int q;
int l[MAXN];
int lg[MAXN];
int sum[20][MAXN];
int prev[20][MAXN];
int* minl=prev[0];

int ReadInt();
int Calc(int,int);

int main(){
    n=ReadInt();
    for(int i=2;i<=n;i++)
        l[i]=ReadInt();
    q=ReadInt();
    l[1]=1;
    minl[n+1]=n;
    for(int i=n;i>=1;i--){
        minl[i]=std::min(l[i],minl[i+1]);
        sum[0][i]=i-minl[i];
    }
    for(int i=1;i<=n;i++)
        sum[0][i]=i-minl[i];
    for(int i=1;(1<<i)<=n;i++){
        lg[1<<i]=1;
        for(int j=1;j<=n;j++){
            prev[i][j]=prev[i-1][prev[i-1][j]];
            sum[i][j]=sum[i-1][j]+sum[i-1][prev[i-1][j]]+((prev[i-1][j]-prev[i][j])<<(i-1));
        }
    }
    for(int i=1;i<=n;i++)
        lg[i]+=lg[i-1];
    for(int i=0;i<q;i++){
        int l=ReadInt(),r=ReadInt(),pos=ReadInt();
        int a=Calc(pos,l)-Calc(pos,r+1);
        int b=r-l+1;
        int gcd=std::__gcd(a,b);
        printf("%d/%d\n",a/gcd,b/gcd);
    }
    return 0;
}

int Calc(int pos,int lim){
    if(l[pos]<lim)
        return pos-lim;
    int dis=0,p=l[pos],ans=pos-lim;
//  printf("x %d %d\n",ans,p);
    for(int i=lg[p];i>=0;i--){
        if(lim<=prev[i][p]){
            ans+=sum[i][p]+(p-prev[i][p])*dis;
//          printf("$ %d Q(%d,%d) %d\n",i,pos,lim,ans);
            p=prev[i][p];
            dis|=(1<<i);
        }
    }
    return ans+(p-lim)*(dis+1);
}

inline int ReadInt(){
    int x=0;
    register char ch=getchar();
    while(!isdigit(ch))
        ch=getchar();
    while(isdigit(ch)){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}

相關文章
相關標籤/搜索