LOJ6435 PKUSC2018星际穿越

Problem

loj

Solution

一个显而易见的结论,对于一个xi,要到达1号节点的最优路径只有第一步可能往右走,后面的步必定是往左走。
那么我们可以dp出整个右边可以走到的最左边的位置mn[i],mn[i]显然小于i,而且如果这个最优位置不是出自i本身,那么也就意味着要往右走一步,再往左走。
为什么mn[i]一定是最优的转移,难道不可能忽略一个中间可以到达的更优的进入情况吗?因为mn[i]也必定是从它dp出的mn[mn[i]],由于中间的点都必定已经经过被dp完了,所以是已经考虑过了的。
先dp出最优的位置,然后i就继承mn[i]的答案,做一个区间修改即可,可以用主席树实现操作,但是空间开销会稍微大一点。
有一个改进方法但是需要离线询问,注意到这个连边最后是一个树形结构,将询问按照dfs序来排序,然后对这棵树进行dfs回答询问。

Code

#include <algorithm>
#include <cstdio>
#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(m<R) update(m+1,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(m<R) res+=query(m+1,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(ql<l[x]) ans+=query(1,n,ql,min(qr,l[x]-1),rt[l[x]]);
        d=gcd(ans,len);
        printf("%d/%d\n",ans/d,len/d);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80726327