题意:有一张边权为 111 的无向图,对 i∈[2,n]i\in [2,n]i∈[2,n],iii 与 [li,i−1][l_i,i-1][li,i−1] 间有边。 qqq 次询问 l,r,xl,r,xl,r,x,表示 xxx 与 [l,r][l,r][l,r] 中的所有点的最短路长度的平均值,其中 l<r<xl<r<xl<r<x。
n,q≤3×105n,q\leq 3\times 10^5n,q≤3×105
考场上以为只能往左走,喜提 0 分。
首先结论是最多在开始时往右跳一步。如果跳了多步,因为你最终要跳回来,那么一定有一次是跨过了出发点的,因为是双向边,所以不如一次跳到这个点然后往左跳。
先考虑第一步往右的情况,设 pre(i,j)pre(i,j)pre(i,j) 表示从 [i,n][i,n][i,n] 中任意一点往左跳最多 jjj 步能走到的最左边的点。尽管 [i+1,n][i+1,n][i+1,n] 中有些点可能无法从 iii 一步跳到,但就意味着这些点还要再跳一次才能跳到 iii 左边,一定是不优的。
由于第一步可以往左,我们再加一个第一步强制往左的,也就是从 lxl_xlx 开始转移。
发现这个有结合性,所以用倍增优化即可。
复杂度 O((n+q)logn)\Omicron((n+q)\log n)O((n+q)logn)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 300005
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){return b? gcd(b,a%b):a;}
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
int a[MAXN],pre[MAXN][20];
ll sum[MAXN][20];
ll query(int p,int x)
{if (a[x]<=p) return x-p;int pos=a[x],cur=1;ll ans=x-a[x];for (int i=19;i>=0;i--)if (pre[pos][i]>=p){ans+=sum[pos][i]+(ll)(pos-pre[pos][i])*cur;pos=pre[pos][i];cur+=(1<<i);}return ans+(ll)(pos-p)*(cur+1);
}
int main()
{int n=read();pre[n+1][0]=2e9;for (int i=2;i<=n;i++) a[i]=read();for (int i=n;i>=1;i--) sum[i][0]=i-(pre[i][0]=min(pre[i+1][0],a[i]));for (int j=1;j<20;j++)for (int i=1;i<=n;i++){pre[i][j]=pre[pre[i][j-1]][j-1];sum[i][j]=sum[i][j-1]+sum[pre[i][j-1]][j-1]+(1ll<<(j-1))*(pre[i][j-1]-pre[i][j]);}for (int q=read();q;q--){int l,r,x;l=read(),r=read(),x=read();ll ans=query(l,x)-query(r+1,x),y=r-l+1;ll d=gcd(ans,y);ans/=d,y/=d;printf("%lld/%lld\n",ans,y);}return 0;
}