1. 程式人生 > >「PKUSC2018」星際穿越 [倍增]

「PKUSC2018」星際穿越 [倍增]

「PKUSC2018」星際穿越

Tags: 倍增 DP


「PKUSC2018」星際穿越

題意

不好概括就不概括啦qwq?

分析

考慮離線,把所有詢問掛在x上。
然後對於當前的x,往左邊所有的值一定是連續遞增的。

然後又想到考場上yy的一個玄妙的主席樹寫法…不過實在是很玄妙現在也寫不出來…?

然後是一個考場上想出來的東西

最多往右走一次

然後首先就是因為一開始打的暴力就不是標準的暴力所以完全沒往倍增上想…反而滿腦子都是主席樹(雖然我jio得可以寫)


首先是一個暴力。記f[x][i]為從x點走到i點的最小步數。這個處理是

n 2 的,然而能拿到70pts。


然後考慮如何優化。對於上面的陣列,下標是位置,存的是代價。然而可以想到代價一定是一條從x往左遞減的序列。
這裡考慮將下標和內容換過來,即 f [ x

] [ i ] 表示的是從x出發向左走i步的最遠左端點。
然後考慮做一個字首和一樣的東西,就可以求出 i =
l x d i s [ x ] [ i ]

之後用倍增優化一下就好了。

感覺說的亂七八糟的qwq 這個題解講的講的挺好的qwq

code

#include<bits/stdc++.h>
using namespace std;
#define M 300005
#define K 21
#define ll long long
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}

int f[M][K];
ll sum[M][K];
void Min(int &x,int y){
    if (x>y)x=y;
}
int g[M],l[M],r[M],x[M];
ll cal(int x,int l){
    if (g[x]<=l)return x-l;
    ll res=x-g[x],base=1;
    x=g[x];
    int k;
    for (k=K-1;k>=0;k--)if (f[x][k]>l){
        res+=sum[x][k]+(x-f[x][k])*base;
        x=f[x][k];
        base+=(1<<k);
    }
    return res+(x-l)*(base+1);
}
int main(){
//  freopen("1.in","r",stdin);
    int n,m,i,L,k;
    ll a,b,gcd;
    read(n);
    for (i=2;i<=n;i++){
        read(g[i]);
    }
    read(m);
    for (i=1;i<=m;i++){
        read(l[i]); read(r[i]); read(x[i]);
    }
    L=n;
    for (i=n;i>=1;i--){
        Min(L,g[i]);
        f[i][0]=L;
        sum[i][0]=(i-L);
    }
    for (k=1;k<K;k++){
        for (i=1;i<=n;i++)if (f[i][k-1]){
            f[i][k]=f[f[i][k-1]][k-1];
            sum[i][k]=sum[i][k-1]+sum[f[i][k-1]][k-1]+(f[i][k-1]-f[i][k])*(1ll<<(k-1));
        }
    }
    for (i=1;i<=m;i++){
        a=cal(x[i],l[i])-cal(x[i],r[i]+1);
        b=r[i]-l[i]+1;
        gcd=__gcd(a,b);
        a/=gcd; b/=gcd;
        printf("%lld/%lld\n",a,b);
    }
    return 0;
}