为了方便,定义。
首先做一次差分,将变成
问题转换为给定,求
不要从最短路的方向考虑,而是考虑从从点走步能够到达的点是哪些
首先,考虑,显然能够到达的点是,并且,是最少的可能步数,于是的最短路已经钦定为了,注意到只有这些点到的步数是,于是以后的任何点到的最短路绝对不是
考虑,(敲黑板),我们巧妙地定义数组,表示的点走一步能够走到的最左的点,显然代表这个,之后你就会发现它非常有用。
注意到有一个特殊的性质,$\forall x \in [m[i],p[i]-1] p[i]->x$到达(也是废话,但是最容易忽略)
我们接着证明的所有点都可以通过条道路从到达:
1.若
那么可以通过两条道路
到达。
如果这里没有明白请回去看一下刚才说的性质。
2.若,注意到我们说的性质,$\forall y \in [m[a[x]],p[a[x]]-1] p[a[x]->y\because x<p[a[x]] \therefore$ 存在的道路。于是我们直接从,再即可通过两次到达。
3.若 ,显然这样是不行的,因为。
考虑,记,所以有的点可以通过次到达。
考虑,记,所以有的点可以通过次到达。
……………………
最终这个最短路序列被分成$[a[x],x-1],[m[a[x]],a[x]-1],[m[m[a[x]]],m[a[x]]-1] , \cdots , [1, ...] $等部分,他们对答案的贡献分别是
不妨考虑换一种方法记录这个答案,这个答案等价于$x-1+(a[x]-1)+(m[a[x]]-1)+(m[m[a[x]]]-1)+(m[m[m[a[x]]]]-1) \cdots $
发现了什么,嵌套的格式让我们想起了倍增,于是我们记录,代表的值,其中嵌套了个
同样,也要记录,代表上面那个东西的前缀和。
到了最终的问题,如何算出,考虑到我们在上面的计算过程中重复算了很多值,我们现在要一个一个减去,记,只有嵌套次的才会对答案造成贡献,于是我们只要求出前个即可。
而且注意到我们重复算了很多东西,对于的数,每次都会被重复算,于是减去即可。
这样就完了。
注意代码里面有一个小特判,如果,那么每个数都可以通过一次调到,答案就是
#include <bits/stdc++.h>
#define MAXN 300005
#define LOG 25
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int a[MAXN],m[MAXN];
int to[MAXN][LOG],sum[MAXN][LOG];
inline int Calc(int l,int r){//sigma_{i=l}^r-1 dist(i,r)
if (a[r]<=l) return (r-1)-l+1;
int s=r-1,p=a[r],cnt=0;
for (register int i=LOG-1;i>=0;--i){
if (to[p][i]>l){//可以跳
cnt|=(1<<i);
s+=sum[p][i];
p=to[p][i];
}
}
s+=(p-1),cnt+=2;
return s-cnt*(l-1);//去掉前面部分
}
int main(){
int n=read();
for (register int i=2;i<=n;++i) a[i]=read();
m[n]=a[n];
for (register int i=n-1;i>=1;--i){
m[i]=min(m[i+1],a[i]);
}
for (register int i=1;i<=n;++i) to[i][0]=m[i],sum[i][0]=i-1;
for (register int j=1;j<LOG;++j){
for (register int i=1;i<=n;++i){
to[i][j]=to[to[i][j-1]][j-1];
sum[i][j]=sum[i][j-1]+sum[to[i][j-1]][j-1];
}
}
int q=read();
while (q--){
int l=read(),r=read(),x=read();
int ans=Calc(l,x)-Calc(r+1,x);
int down=r-l+1;
int g=__gcd(ans,down);
printf("%d/%d\n",ans/g,down/g);
}
}