zoukankan      html  css  js  c++  java
  • [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增)

    [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增)

    题面

    n个点的图,点i和[l[i],i)的所有点连双向边。每次询问(l,r,x)表示x到[l,r]的所有点的最短路径长度和/(r-l+1)。

    (n leq 10^5,l_i<r_i<x_i)

    分析

    有(du)趣(liu)的倍增问题。

    观察到(l_i<r_i<x_i),也就是说我们要求往左走的最小距离。首先差分一下,设(sum(x,i))表示(x)([i,x-1])的最短距离和。那么答案就是(frac{sum(x,l)-sum(x,r+1)}{r-l+1})

    然后我们不要直接考虑到x的最短路,而是反过来考虑走k步可以到达哪些节点。

    我们从x开始走1步,可以走到哪里呢?。可以跳到的编号最小的点为(l_x),而最大的点应该是(rb(x)=max(k) (l_k leq x)),因为只要(l_k leq x), (x)(k)之间就有一条边。因此第1步能到达的区间是([l_x,rb(x)])

    第2步呢?。根据上面的分析,应该是(min(l_i)(i in [l_x,rb(x)])).但是实际上可以简化为(min(l_i)(i in [l_x,n])).因为在([rb(x),n])内的点,它们的(l)比x还大,自然也比([l_x,rb(x)])内的点的(l)还大,对最小值没有任何影响。

    因此,设走(k(k>1))步可以到达的编号最小的点为(a),那么走(k+1)步能够到达的编号最小的点为(min(l_i)(iin [a,n])).这样就可以倍增优化。

    (f[i][j])表示([i,n])内节点走(2^j)步能够到达的编号最小的点。那么显然有:

    [f[i][0]=min(l_k),k in[i,n] ]

    [f[i][j]=f[f[i][j-1]][j-1] ]

    为了求答案,我们再维护一个和,(g[i][j])表示(i)([f[i][j],i-1])内所有点的距离和。那么:

    (g[i][0]=i-f[i][0]) (到([f[i][0],i-1])中的每个点的距离都为1)

    [g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+2^{j-1}(f[i][j-1]-f[i][j]) ]

    这是因为:到([f[i][j-1],i-1])内的节点,距离和是(g[i][j-1])。而到([f[i][j],f[i][j-1]])内的节点的距离由两部分组成,一部分是各节点到(f[i][j-1])的距离(g[f[i][j-1]][j-1]),另一部分是从(f[i][j-1])(i)的距离(2^{j-1}).因为一共有((f[i][j-1]-f[i][j]))个节点,所以要乘上((f[i][j-1]-f[i][j]))

    查询的话就倍增的跳即可,注意一些细节,还是直接上代码吧.

    int calc(int x,int l){ //sum(x,l),[l,x-1]内答案 
    	if(a[x]<=l) return x-l;//只需走一次的情况,特判
    	//第一次走的答案 
    	int ans=x-a[x];//距离和
    	int cnt=1;//走的次数
    	x=a[x]; 
    	for(int i=log2n;i>=0;i--){
    		if(f[x][i]>=l){
    			ans+=g[x][i]+cnt*(x-f[x][i]);//,g[x][i]为当前这段的距离和,但是之前每个节点还跳了cnt步到x,因此要加上cnt*[f[x][i],x-1]
    			cnt+=(1<<i);
    			x=f[x][i];
    		}
    	}
    	if(x>l) ans+=x-l+cnt*(x-l);//如果最后一步没跳满,加上f[x][i] 
    	return ans; 
    } 
    

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath> 
    #define maxn 300000
    #define maxlogn 25 
    using namespace std;
    int gcd(int a,int b){
    	return b==0?a:gcd(b,a%b);
    }
    int n,q;
    int log2n;
    int a[maxn+5]; 
    int f[maxn+5][maxlogn+5];
    int g[maxn+5][maxlogn+5];
    int calc(int x,int l){ //[l,x-1]内答案 
    	if(a[x]<=l) return x-l;
    	//特判第一次跳 
    	int ans=x-a[x];
    	int cnt=1;
    	x=a[x]; 
    	for(int i=log2n;i>=0;i--){
    		if(f[x][i]>=l){
    			ans+=g[x][i]+cnt*(x-f[x][i]);//[f[x][i],x-1]跳到x还需cnt步 
    			cnt+=(1<<i);
    			x=f[x][i];
    		}
    	}
    	if(x>l) ans+=x-l+cnt*(x-l);//如果最后一步没跳满,加上f[x][i] 
    	return ans; 
    } 
    int main(){
    	int l,r,x;
    	scanf("%d",&n);
    	log2n=log2(n)+1;
    	a[1]=1;
    	for(int i=2;i<=n;i++) scanf("%d",&a[i]);
    	f[n][0]=a[n];
    	for(int i=n-1;i>=1;i--){
    		f[i][0]=min(f[i+1][0],a[i]);
    		g[i][0]=i-f[i][0];
    	}
    	for(int j=1;j<=log2n;j++){
    		for(int i=1;i<=n;i++){
    			if(f[i][j-1]){
    				f[i][j]=f[f[i][j-1]][j-1];
    				g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1]+((f[i][j-1]-f[i][j])<<(j-1)); 
    				//[f[i][j],f[i][j-1]-1]内的点跳到j还需2^{j-1}步 
    			}
    		}
    	}
    	scanf("%d",&q);
    	while(q--){
    		scanf("%d %d %d",&l,&r,&x);
    		int up=calc(x,l)-calc(x,r+1);
    		int down=r-l+1;
    		int g=gcd(up,down);
    		up/=g;
    		down/=g;
    		printf("%d/%d
    ",up,down);
    	}
    }
    
    
  • 相关阅读:
    Expression Blend实例中文教程(13)
    Expression Blend实例中文教程(12)
    Expression Blend实例中文教程(11)
    【转】Python3中urllib详细使用方法(header,代理,超时,认证,异常处理)
    cookie
    【转】cookielib模块
    代理的步骤
    urllib.parse.urlencode
    urllib.request.Request
    【转】python3 urllib.request 网络请求操作
  • 原文地址:https://www.cnblogs.com/birchtree/p/11521717.html
Copyright © 2011-2022 走看看