1 条题解

  • 0
    @ 2026-10-31 20:31:30

    迁徙

    最短路径结构

    若路径出现局部折返,前后两点距离不超过其中一条边,可跳过折返点得到更短路径。因此最短路径严格自北向南。

    用双指针求r[i]:从i一次最远可到的下标。r单调不减。固定起点,最短路第k层为(r^(k-1)(s),r^k(s)]。同层内某点的最短路条数,是上一层所有能到达它的点的路径条数之和;因r单调,贡献者构成上一层后缀,所以同层条数从左向右单调不增。

    截断状态

    只关心恰好2条,条数≥3可统一截为3。所有可达前驱至少贡献1,所以只需看上一层最右两点;若最右三个都能到达,条数必≥3。状态分三类:

    • 0:最右点1条,倒数第二点也1条(若本层仅一个点,按此类处理);
    • 1:最右点1条,倒数第二点至少2条;
    • 2:最右点2条。此时倒数第二点的具体数值不重要。

    最右点已≥3时,后续所有层都≥3,可立即停止贡献。

    令当前层为(p,z],z=r[p],下一层为(z,e],e=r[z]。令w=z-p。状态2时,仅最后一个前驱贡献的区间才恰好2条,数量为e-max(z,r[z-1]);若w=1,则以z代替r[z-1]。状态0且w≥2时,恰好最后两个前驱贡献的区间才恰好2条,数量为max(0,r[z-1]-max(z,r[z-2]));若w=2,以z代替r[z-2]。状态1无恰好2条的终点。

    为了确定下一层状态,对e和e-1分别检查最右三个前驱是否覆盖:有三个则3;有两个则将两条数相加截为3;只有一个则继承最右条数。层大小由w与e-z可知。

    动态规划

    dp[p][state]表示当前层(p,r[p]]处于state时,后续所有层的双径终点数。转移到dp[r[p]][nextState],再加本次贡献。r[p]>p时依赖下标更大,从右到左即可;不能向前推进时为0。

    任意起点s的第一层每个点都只有一条直接路径,初始状态是0,所以答案是Σdp[s][0]。每个dp值≤n,可用int;总答案可能约10^12,必须long long。

    时间O(N),空间O(N)。独立oracle对无向距离图从每个起点BFS并计数,未复用本算法的分层三状态转移。大规模还核对了两条长链通过一个菱形连接的L*R解析答案。

    难度依据

    7 / geekpi-9-v2:利用最短路分层计数单调性,只保留最右两点的三类截断状态,在线性复杂度内统计所有起点。

    参考程序

    #include <cstdio>
    #include <vector>
    #include <array>
    #include <algorithm>
    using namespace std;
    struct Fast{static const int Z=1<<20;char b[Z];int p=0,n=0;char get(){if(p==n){n=fread(b,1,Z,stdin);p=0;if(!n)return 0;}return b[p++];}long long read(){char c;do c=get();while(c&&c<=32);bool neg=c=='-';if(neg)c=get();long long x=0;while(c>='0'&&c<='9'){x=x*10+c-'0';c=get();}return neg?-x:x;}}in;
    int main(){in.read();int T=in.read();while(T--){int n=in.read();long long d=in.read();vector<long long>x(n);for(auto&v:x)v=in.read();vector<int>r(n);int j=0;for(int i=0;i<n;i++){j=max(j,i);while(j+1<n&&x[j+1]-x[i]<=d)j++;r[i]=j;}vector<array<int,3>>dp(n);long long ans=0;for(int p=n-1;p>=0;p--){int z=r[p],w=z-p,e=r[z],nw=e-z;if(w==0||nw==0)continue;for(int st=0;st<3;st++){int A=st==2?2:1;auto countAt=[&](int v){if(w>=3&&r[z-2]>=v)return 3;if(w>=2&&r[z-1]>=v)return st==0?2:3;return A;};int a=countAt(e),b=nw>=2?countAt(e-1):0,cnt=0;if(st==2)cnt=e-max(z,w>=2?r[z-1]:z);else if(st==0&&w>=2)cnt=max(0,r[z-1]-max(z,w>=3?r[z-2]:z));if(a<3)cnt+=dp[z][a==2?2:((b==0||b==1)?0:1)];dp[p][st]=cnt;}ans+=dp[p][0];}printf("%lld\n",ans);}}
    

    本参考解为独立编写的训练参考解,非官方标准程序。测试数据为自建训练数据。

    • 1