loj #6435. 「PKUSC2018」星际穿越

版权声明:本文为博主原创文章,爱转载的表明出处就好. https://blog.csdn.net/qq_36797743/article/details/81334381

题意

自己看吧

前言

pkusc这题的阴影太大,现在才做回。。
现在想起来,自己的方法过于复杂,并且忘记了使用标记永久化使得代码量巨增。。调不出来也是有原因的

题解

相比起我原来的方法,现在有一个很巧妙的
显然的结论,一个点只会往右边跳一次
然后我们发现这个跳一次非常地难搞。。
考场上我写得是两颗线段树搞来搞去。。搞得我头都大了
我们考虑,其实往右跳除了第一次都是不用代价的,因为你可以直接跳到这个点的右边啊。
因此,我们我们对于一个点,记录一个 m n ,表示他(可以先右跳一步)往左跳一步能到的最左
然后如果往右跳不算次数的话,直接从 m n 这里转移过来就可以了
至于查询的时候,也挺巧妙的
我们查询的是 r o o t [ l [ x ] ]
我们假设先跳到这里,如果他往右跳了,那么就相当于第一步跳到那里就可以了
当然,答案要加上 r l + 1

CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=300005;
int n,q;
int a[N];
int mn[N];
LL c[N*40],lzy[N*40];
int s1[N*40],s2[N*40];
int num=0;
void change (int &now,int l,int r,int L,int R)
{
    num++;
    c[num]=c[now];lzy[num]=lzy[now];s1[num]=s1[now];s2[num]=s2[now];
    now=num;
    if (l==L&&r==R)
    {
        lzy[now]++;
        return ;
    }
    c[now]=c[now]+(R-L+1);
    int mid=(l+r)>>1;
    if (R<=mid) change(s1[now],l,mid,L,R);
    else if (L>mid) change(s2[now],mid+1,r,L,R);
    else change(s1[now],l,mid,L,mid),change(s2[now],mid+1,r,mid+1,R);
}
LL ans;
void ask (int now,int l,int r,int L,int R)
{
    //printf("YES:%lld %lld %lld %lld %lld %lld\n",l,r,L,R,lzy[now],c[now]);
    ans=ans+lzy[now]*(R-L+1);
    if (l==L&&r==R) {ans=ans+c[now];return ;}
    int mid=(l+r)>>1;
    if (R<=mid) ask(s1[now],l,mid,L,R);
    else if (L>mid) ask(s2[now],mid+1,r,L,R);
    else ask(s1[now],l,mid,L,mid),ask(s2[now],mid+1,r,mid+1,R);
}
int rt[N];
LL gcd (LL x,LL y)
{
    return x==0?y:gcd(y%x,x);
}
int read()
{
    char ch=getchar();int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
int main()
{
n=read();
    for (int u=2;u<=n;u++)  a[u]=read();
    mn[n]=a[n];
    for (int u=n-1;u>=1;u--) mn[u]=min(a[u],mn[u+1]);
    for (int u=2;u<=n;u++)
    {
        rt[u]=rt[mn[u]];
        change(rt[u],1,n,1,u-1);
    }
    q=read();
    for (int u=1;u<=q;u++)
    {
        int l,r,x;
        l=read();r=read();x=read();
        ans=(r-l+1);
        if (l<a[x]) ask(rt[a[x]],1,n,l,min(r,a[x]-1));
        LL b=(r-l+1);
        LL d=gcd(b,ans);
        printf("%lld/%lld\n",ans/d,b/d);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36797743/article/details/81334381