• [PKUSC2018]星际穿越——可持久化线段树+DP


    题目描述

    有n个星球,它们的编号是1到n,它们坐落在同一个星系内,这个星系可以抽象为一条数轴,每个星球都是数轴上的一个点,
    特别地,编号为i的星球的坐标是i。
    一开始,由于科技上的原因,这n个星球的居民之间无法进行交流,因此他们也不知道彼此的存在。
    现在,这些星球独立发展出了星际穿越与星际交流的工具。
    对于第i个星球,他通过发射强力信号,成功地与编号在[Li,i-1]的所有星球取得了联系(编号为1的星球没有发出任何信号),
    取得联系的两个星球会建立双向的传送门,对于建立了传送门的两个星球u,v,u上的居民可以花费1单位时间传送到v,
    v上的居民也可以花费1单位时间传送到u,我们用dist(x,y)表示从编号为x的星球出发,通过一系列星球间的传送门,
    传送到编号为y的星球最少需要花费的时间。
    现在有q个星际商人,第i个商人初始所在的位置是xi,他的目的地是[Li,Ri]中的其中一个星球,保证Li<Ri<xi。
    他会在这些星球中等概率挑选一个星球y(每个星球都有一样的概率被选中作为目的地),
    然后通过一系列星球的传送门,花费最少的时间到达星球y。
    商人想知道他花费的期望时间是多少?也就是计算∑dist(xi,y)/(Ri-Li+1),其中y<=Li<=Ri

    输入

    第一行一个正整数n,表示星球的个数。
    第二行n-1个正整数,第i个正整数为Li+1,
    表示编号在[Li+1,i]区间内所有星球已经与编号为i+1的星球取得了联系,并且可以通过花费1单位进行彼此的传输。保证Li+1≤i
    第三行一个正整数q,表示询问组数。
    接下来q行,每行三个数字Li,Ri,xi,表示在[Li,Ri]这个区间中等概率选择一个星球y,dist(xi,y)的期望。
    保证Li<Ri<xi,n,q≤3×10^5

    输出

    对于每组询问,注意到答案必然是一个有理数,因此以p/q的格式输出这个有理数,要求gcd(p,q)=1
    如果答案为整数m,输出m/1

    样例输入

    7
    1 1 2 1 4 6
    5
    3 4 6
    1 5 7
    1 2 4
    1 2 6
    1 3 5

    样例输出

    3/2
    13/5
    3/2
    2/1
    1/1
     
    首先发现一个性质,一个点往右最多只能走一步,因为走两步到达的点的l[i]一定大于走一步或者不走时的l[i]。
    那么可以DP出mn[i]表示i点往右走一步或不往右走后能一步走到的最左边的点。
    可以发现如果不考虑往右走一步的代价,每个点与前面每个点的最短路中代价相同的点是连续的一段。
    这样每一回只要往右走到mn[i]就能最小化代价。
    为什么不能是先走到中间某个点然后再能走到更远的点?
    因为mn[i]是从后往前DP的,所以中间点会更新之前点的mn[i]。
    对于每个点开一棵可持久化线段树存这个点到达每个点的最短路(也可以离线之后用线段树)。
    实际上对于第i棵可持久化线段树存的是i之前每个点到达更新mn[i]的那个点的最短路。
    对于点i,它到前面点的代价是mn[i]到每个点的代价+1。但这样没有考虑往右走一步的代价,因此每次查询时要把答案加上区间长。
    可持久化线段树区间修改要标记永久化。
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    inline char _read()
    {
        static char buf[100000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    }
    inline int read()
    {
        int x=0,f=1;char ch=_read();
        while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=_read();}
        while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=_read();}
        return x*f;
    }
    int n,m;
    int cnt;
    int ans;
    int len;
    int x,y,z;
    int l[300010];
    int mn[300010];
    int ls[8000010];
    int rs[8000010];
    int sum[8000010];
    int num[8000010];
    int root[300010];
    int gcd(int x,int y)
    {
        if(y==0)
        {
            return x;
        }
        return gcd(y,x%y);
    }
    void change(int &rt,int l,int r,int L,int R,int v)
    {
        int now=++cnt;
        ls[now]=ls[rt];
        rs[now]=rs[rt];
        sum[now]=sum[rt];
        num[now]=num[rt];
        rt=now;
        if(L<=l&&r<=R)
        {
            num[rt]+=v;
            return ;
        }
        sum[rt]+=(min(r,R)-max(l,L)+1)*v;
        int mid=(l+r)>>1;
        if(L<=mid)
        {
            change(ls[rt],l,mid,L,R,v);
        }
        if(R>mid)
        {
            change(rs[rt],mid+1,r,L,R,v);
        }
    }
    int query(int rt,int l,int r,int L,int R)
    {
        if(!rt)
        {
            return 0;
        }
        int res=num[rt]*(min(r,R)-max(l,L)+1);
        if(L<=l&&r<=R)
        {
            return res+sum[rt];
        }
        int mid=(l+r)>>1;
        if(L<=mid)
        {
            res+=query(ls[rt],l,mid,L,R);
        }
        if(R>mid)
        {
            res+=query(rs[rt],mid+1,r,L,R);
        }
        return res;
    }
    int main()
    {
        n=read();
        for(int i=2;i<=n;i++)
        {
            l[i]=read();
            mn[i]=l[i];
        }
        for(int i=n-1;i>=1;i--)
        {
            mn[i]=min(mn[i+1],l[i]);
        }
        for(int i=2;i<=n;i++)
        {
            root[i]=root[mn[i]];
            change(root[i],1,n,1,i-1,1);
        }
        m=read();
        for(int i=1;i<=m;i++)
        {
            x=read();
            y=read();
            z=read();
            ans=len=y-x+1;
            if(x<l[z])
            {
                ans+=query(root[l[z]],1,n,x,min(y,l[z]-1));
            }
            int res=gcd(ans,len);
            printf("%d/%d
    ",ans/res,len/res);
        }
    }
  • 相关阅读:
    windows 全局安装 composer【转】
    CentOS7使用firewalld打开关闭防火墙与端口
    转 Page Object模式
    转 用SQL语句,删除掉重复项只保留一条
    转 什么是Mbps、Kbps、bps、kb、mb及其换算和区别
    转 使用Python的logging.config.fileConfig配置日志
    转 zookeeper,dubbo和Nginx的区别
    转 lsof命令详解
    Fatal Error -26000: Not enough memory (12320 bytes) for “new buffer in LrwSrvNetTaskIt 问题解决及lr脚本心得
    Action.c(28): Error -27796: Failed to connect to server "xxxx": [10060] Connection timed out
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9629188.html
Copyright © 2020-2023  润新知