PKUSC 2018 星际穿越

这是一道有难度的倍增题。

因为rili+1可以直接计算,故只要求出j=liridist(xi,j),就可以回答询问了。

我们设farthest(i,j)表示从i出发,走j步之内最远能到达哪些点。

接下来我们证明一个结论:farthest(i,j)=mink=farthest(i,j1)nlkfarthest(i,0)=li

首先我们发现,最优的路径一定是从x出发,向后走最多一格,然后再一直向前走。

显然走了一步,那么只能往前走,farthest(i,0)=li

考虑走了超过一步,那么我们可以在第一步走到一些i的点S,还有一些点jj虽然大于i,但是lj>i,导致i,j间没有边相连,无法在第一步走到。但是我们仍然可以加入这些点,因为这些点都满足lj>i,而li<i,故farthest(i,p)肯定小于i,这些j并不会对后缀min造成影响。

这样设minli=minj=inlj,那么我们可以从i连一条边到minli,表示i在下一步最远可以到达minli

我们将问题转化成solve(i,x)表示从x出发,走到[i,x)的每个点的最短路的和,那么ans=solve(li,x)solve(ri+1,x)

这样,我们可以一步一步找下一个minli,同时进行计算,是O(n2)的。

但是,我们发现这个问题可以用倍增,设nxt(i,j)表示从i出发,走2j步最远可以到达的点,则nxt(i,j)=nxt(nxt(i,j1),j1)

然后设sum(i,j)=k=nxt(i,j)i1dist(i,k),则不难推出sum(i,j)=sum(i,j1)+sum(nxt(i,j1),j1)+2j1(nxt(i,j1)nxt(i,j))

进行solve函数的过程和预处理倍增的过程基本类似。

要特别注意两点:

  1. 最后一步要特判,因为minl有可能直接跳过了我们规定的最小值。
  2. 第一步也要特判,因为按照我们的推导,nxt(i,0)=minli,但实际上应该是li,因为此时我们还无法到大于i的点上。
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl

using ll=long long;
const int maxn=300005;
const int maxlog=20;
int n,q,l[maxn],minl[maxn];
int nxt[maxn][maxlog];
ll sum[maxn][maxlog];

ll gcd(ll a,ll b) {
	return b==0?a:gcd(b,a%b);
}

ll solve(ll to,ll pos) {
	if(to==pos) return 0;
	ll ret=0,tot=0;
	if(l[pos]>=to) ret+=pos-l[pos],tot=1,pos=l[pos];
	for(int j=maxlog-1;~j;j--) {
		if(nxt[pos][j]>=to) {
			ret+=sum[pos][j]+tot*(pos-nxt[pos][j]);
			pos=nxt[pos][j];
			tot+=1ll<<(ll)j;
		}
	}
	ret+=(pos-to)*(tot+1);
	return ret;
} 

int main() {
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
		scanf("%d",&l[i]);
	minl[n]=l[n];
	for(int i=n-1;i>=2;i--)
		minl[i]=std::min(l[i],minl[i+1]);
	for(int i=1;i<=n;i++) {
		nxt[i][0]=minl[i]; sum[i][0]=i-minl[i];
		for(int j=1;j<maxlog;j++) {
			nxt[i][j]=nxt[nxt[i][j-1]][j-1];
			sum[i][j]=sum[i][j-1]+sum[nxt[i][j-1]][j-1]+(1ll<<ll(j-1))*(nxt[i][j-1]-nxt[i][j]);
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=0;j<maxlog;j++) {
			if(!nxt[i][j]) sum[i][j]=0;
		}
	}
	scanf("%d",&q);
	while(q--) {
		int lef,rig,x;
		scanf("%d%d%d",&lef,&rig,&x);
		ll a=solve(lef,x)-solve(rig+1,x),b=rig-lef+1,c=gcd(a,b);
		printf("%lld/%lld\n",a/c,b/c);
	}
	return 0;
}
posted @   Nastia  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示