P5465 [PKUSC2018]星际穿越 题解
题目描述
\(n\) 个星球,第 \(i\) 个星球和 \([l_i,i-1]\) 中的每个星球之间有一条双向边, \(dis(x,y)\) 为第 \(x\) 个星球到第 \(y\) 个星球的距离。
\(q\) 次询问,给定 \(l,r,x\) ,求 \(\frac{\sum_{i=l}^rdis(x,i)}{r-l+1}\) 的值。
数据范围
- \(1\le n,q\le 3\cdot 10^5,l\lt r\lt x\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{500MB}\) 。
分析
别被骗了,这并不是一道图论题。
性质一:若 \(x\gt y\) ,则 \(\forall y\le z\le x\) ,有 \(dis(x,z)\le dis(x,y)\) 。
废话,无论如何从 \(x\) 走到 \(y\) ,一定会经过中间所有节点。
性质二:若 \(x\gt y\) ,则 \(x\to y\) 的最短路至多只会向右走一次;如果向右走,只可能是第一步走到 \(l_z\lt x\) 的点 \(z\) 。
考虑 \(x\to y\) 路径上编号最大(最靠右)的点。
如果这个点为 \(x\) ,假设中间某一步 \(u\to v\) 向右走,根据性质一, \(x\to u\to v\to y\) 一定没有 \(x\to v\to y\) 优。
如果这个点为 \(z\gt x\) ,那么到达 \(z\) 这一步是向右的。
根据前面的分析,\(z\to y\) 是不会 "绕路" 的,对于 \(x\to z\) 的路径:
-
如果 \(l_z\le x\) ,显然 \(dis(l_z,x)=1\) 。
-
如果 \(l_z\gt x\) ,我们证明此时一定不是最短路径。
由于 \(y\lt x\lt z\) ,所以 \(z\to y\) 时一定经过了某个点 \(z'\) 满足 \(l_{z'}\le x\) ,路径 \(x\to z'\to y\) 一定比 \(x\to z\to y\) 更优。
用上面的结论预处理 \(dis(x,y)\) ,时间复杂度 \(\mathcal O(n^2)\) ,可以获得 \(70pts\) 。
\(dis=1\) 是平凡的。
\(dis=2\) 时,从 \(x\) 出发,能走到的编号最小的点是什么?
可以证明是 \(\min\limits_{l_x\le i\le n}l_i\) ,分第一步向左还是向右讨论一下就行。
进一步,假设 \(k\ge 2\) 步能走到的编号最小的点为 \(y\) ,那么\(k+1\)步能走到的编号最小的点为\(\min\limits_{l_y\le i\le n}l_i\) 。
然后考虑倍增。
\(f_{i,j}\) 为从 \([i,n]\) 出发,跳 \(2^j\) 步能到达的最小编号。
\(g_{i,j}=\sum_{k=f_{i,j}}^{i-1}dis([i,n],k)\) ,其中 \(dis([i,n],k)\) 表示 \([i,n]\) 中某个点到 \(k\) 的距离的最小值。
转移是简单的:
注意定义不是从 \(i\) 出发,比如下面这组数据:
7 1 2 1 2 5 3\(f_{6,1}=1\) ,但是 \(dis(6,1)=3,dis([6,7],1)=2\) 。
再来考虑怎么查询,还是倍增。
首先把询问差分成 \(\sum_{k=l}^{i-1}dis(i,k)-\sum_{k=r+1}^{i-1}dis(i,k)\) 。
先强制跳一步 \(l_i\) ,然后给每个 \(\lt l_i\) 的点的 \(dis\) 都加上 \(1\) ,此后可以把 \([l_i,n]\) 都当成终点!
那么接下来的问题形如 \(\sum_{k=r}^xdis([x,n],k)\) ,这个就可以倍增用g数组求解了。
倒序枚举 \(j\) ,维护已经花费的步数和已经知道最短路的后缀部分的距离和即可。
时间复杂度 \(\mathcal O(n\log n)\) 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+5,inf=1e9;
int n,q;
int l[maxn],suf[maxn];
ll f[maxn][19],g[maxn][19];
ll calc(int r,int x)
{
if(l[x]<=r) return x-r;
int cnt=1,cur=l[x];
ll res=x-l[x];
for(int j=18;j>=0;j--)
if(f[cur][j]>=r)
{
res+=g[cur][j]+(cur-f[cur][j])*cnt;
cnt+=1<<j,cur=f[cur][j];
}
res+=(cur-r)*(cnt+1ll);
return res;
}
ll gcd(ll a,ll b)
{
if(!b) return a;
return gcd(b,a%b);
}
int main()
{
scanf("%d",&n),suf[n+1]=inf;
for(int i=2;i<=n;i++) scanf("%d",&l[i]);
for(int i=n;i>=1;i--) suf[i]=min(suf[i+1],l[i]);
for(int i=1;i<=n;i++)
{
f[i][0]=suf[i],g[i][0]=i-suf[i];
for(int j=1;j<=18;j++)
{
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));
}
}
scanf("%d",&q);
for(int i=1,l,r,x;i<=q;i++)
{
scanf("%d%d%d",&l,&r,&x);
ll up=calc(l,x)-calc(r+1,x),dn=r-l+1,g=gcd(up,dn);
printf("%lld/%lld\n",up/g,dn/g);
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16482462.html
浙公网安备 33010602011771号