loj
一个显而易见的结论,对于一个xi,要到达1号节点的最优路径只有第一步可能往右走,后面的步必定是往左走。
那么我们可以dp出整个右边可以走到的最左边的位置mn[i],mn[i]显然小于i,而且如果这个最优位置不是出自i本身,那么也就意味着要往右走一步,再往左走。
为什么mn[i]一定是最优的转移,难道不可能忽略一个中间可以到达的更优的进入情况吗?因为mn[i]也必定是从它dp出的mn[mn[i]],由于中间的点都必定已经经过被dp完了,所以是已经考虑过了的。
先dp出最优的位置,然后i就继承mn[i]的答案,做一个区间修改即可,可以用主席树实现操作,但是空间开销会稍微大一点。
有一个改进方法但是需要离线询问,注意到这个连边最后是一个树形结构,将询问按照dfs序来排序,然后对这棵树进行dfs回答询问。
#include
#include
#define rg register
using namespace std;
const int maxn=300010,maxm=8000010,INF=0x3f3f3f3f;
int n,m,sz,l[maxn],mn[maxn],rt[maxn];
int lc[maxm],rc[maxm],add[maxm],sum[maxm];
template <typename Tp> inline void read(Tp &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
void update(int l,int r,int L,int R,int val,int &rt)
{
sz++;lc[sz]=lc[rt];rc[sz]=rc[rt];
add[sz]=add[rt];sum[sz]=sum[rt];rt=sz;
if(L<=l&&r<=R){add[rt]+=val;return ;}
sum[rt]+=val*(min(r,R)-max(l,L)+1);
int m=(l+r)>>1;
if(L<=m) update(l,m,L,R,val,lc[rt]);
if(m1,r,L,R,val,rc[rt]);
}
int query(int l,int r,int L,int R,int rt)
{
if(!rt) return 0;
int res=add[rt]*(min(r,R)-max(l,L)+1);
if(L<=l&&r<=R) return res+sum[rt];
int m=(l+r)>>1;
if(L<=m) res+=query(l,m,L,R,lc[rt]);
if(m1,r,L,R,rc[rt]);
return res;
}
void input()
{
read(n);
for(rg int i=2;i<=n;i++) read(l[i]);
read(m);mn[n]=l[n];
for(rg int i=n-1;i;i--)
mn[i]=min(mn[i+1],l[i]);//整个右边可以到达的最左边的点,包括考虑了往右边走一步
for(rg int i=2;i<=n;i++)
{
rt[i]=rt[mn[i]];
update(1,n,1,i-1,1,rt[i]);//这里可能少考虑了往右走再跳回来的贡献
}//但是没关系,在后来处理的时候初始化权值时即可
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int ql,qr,x,len,ans,d;
input();
for(rg int i=1;i<=m;i++)
{
read(ql);read(qr);read(x);
ans=len=qr-ql+1;
if(ql1,n,ql,min(qr,l[x]-1),rt[l[x]]);
d=gcd(ans,len);
printf("%d/%d\n",ans/d,len/d);
}
return 0;
}