Loading

P10198 [USACO24FEB] Infinite Adventure P 题解

深刻。

题意

\(n\) 个城市,每个城市 \(i\) 有一个周期 \(T_i\)\(T_i\)\(2\) 的幂次。城市 \(i\)\(T_i\) 个传送门为 \(c_{i,0},\dots,c_{i,T_i−1}\)。若在日期 \(t\) 进入城市 \(i\),则花费一天到达城市 \(c_{i,t\bmod T_i}\)。给定 \(q\) 个询问 \((v,t,\Delta)\),问从日期 \(t\) 出发,在城市 \(v\) 执行 \(\Delta\) 次传送后到达的城市。\(1\leq t,\Delta\leq 10^{18},q\leq 5\times 10^4\)

初步思考

考虑到值域非常大,直接模拟肯定不行,想到倍增。一个自然的想法是定义操作 \(jump(i,r,k)\) 表示在城市 \(i\)\(t\bmod T_i=r\),走 \(2^k\) 步到达的城市。但是这样会有问题。例如当前 \(T_i=4,t\bmod 4=1\),那么 \(t\) 可以是 \(1,5,9,\dots\)。如果下一步走到 \(T_j=8\),那么我们要知道 \((t+1)\bmod 8\),当 \(t=1\) 时等于 \(2\)\(t=5\) 时等于 \(6\),结果不一样。而只有 \(T_j\leq T_i\) 才能确定。而这个题有一个很好的性质就是 \(T_i=2^k\),所以两个周期之间可以整除,所以比 \(T_i\) 小的都可以处理,比它大的信息会丢失。换句话说,如果现在已知 \(t\bmod T_i\),则所有满足 \(T'|T\) 的城市都是确定的,因为 \(t\bmod T'=(t\bmod T)\bmod T'\)

状态建立与转移

我们定义 \(T\) 相同的城市为同层,按经过同层的城市为一步。如果中间有更低层的(\(T_j< T_i\))则可以直接压缩掉,不影响信息,直接到达最近的同层城市。还要记一个真实步数代表实际走了多少个城市,方便更新 \(\Delta\)。即 \(f_{k,u,r}\) 表示从 \(u\) 开始走 \(2^k\) 个同层城市,\(t\bmod T_u=r\) 的位置,\(g_{k,u,r}\) 表示真实步数。在 \(k=0\) 时,根据定义,遇到同一层就记录,如果中途遇到高层的就停下来。那么转移就比较显然,按周期从小到大处理,设 \(v=f_{k-1,u,r},l=g_{k-1,u,r}\),转移为 $$\begin{aligned}f_{k,u,r}&=f_{k-1,v,(r+l)\bmod T_u}\\ g_{k,u,r}&=g_{k-1,v,(r+l)\bmod T_u}+l\end{aligned}$$

查询

对于查询 \((v,t,\Delta)\),维护当前城市 \(x\),日期 \(t'\) 以及剩余步数 \(\Delta'\)。从最大的 \(LOG\) 开始倒着枚举 \(i\),如果 \(g_{i,x,t'\bmod T_x}\leq \Delta'\),说明可以走,更新一下。关键优化,如果更新后仍然满足条件,让 \(i\leftarrow i+1\),相当于保持不变,继续走 \(2^i\) 次。如果所有的都走不动了就手动跳一步,即 \(x=c_{x,t'\bmod T_x}\)。直到 \(\Delta'=0\)

复杂度分析

预处理部分处理 \(f_0,g_0\) 时是 \(O(\sum T_i\cdot \log \max T)\),倍增合并是 \(O(\sum T_i\cdot \log V)\)\(V\)\(\Delta\) 的值域。而查询部分不加优化最坏每一轮可能只能走 \(\log V\) 步,复杂度退化至 \(O(\Delta)\)。加上优化会不停复用一个 \(i\),复杂度是 \(O(\log V)\)。所以总体复杂度 $$O\Big(\big(\sum T_i+q\big)\log V\Big)$$

代码

#include<bits/stdc++.h>
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)

using namespace std;
typedef long long i64;
typedef __int128 i128;

const int N=1e5+5,LOG=61;
const i64 INF=1ll<<60;

void solve();
int n,q,maxT;
vector<int> T,c[N],f[LOG][N];
vector<i64> g[LOG][N];

signed main(){
  int Test=1;
//  scanf("%d",&Test);
  while(Test--) solve();
  return 0;
}

void build(){
  L(i,1,maxT,i){
    L(j,1,n,1){
      if(i!=T[j]) continue;
      L(k,0,T[j]-1,1){
        int x=c[j][k];
        i64 len=1,t=k+1;
        while(T[x]<T[j]){
          i64 y=g[LOG-1][x][t%T[x]];
          if(len+y>=INF) break;
          len+=y;
          x=f[60][x][t%T[x]];
          t+=y;
        }
        f[0][j][k]=x;
        g[0][j][k]=len;
      }
    }
    L(l,1,LOG-1,1){
      L(j,1,n,1){
        if(i!=T[j]) continue;
        L(k,0,T[j]-1,1){
          int x=f[l-1][j][k];
          i64 y=g[l-1][j][k];
          if(T[x]!=T[j]){
            f[l][j][k]=x;
            g[l][j][k]=y;
            continue;
          }
          f[l][j][k]=f[l-1][x][(k+y)%T[x]];
          g[l][j][k]=min(g[l-1][x][(k+y)%T[x]]+y,INF);
        }
      }
    }
  }
}

int query(int x,i64 t,i64 del){
  while(del){
    R(i,LOG-1,0,1){
      int y=f[i][x][t%T[x]];
      i64 w=g[i][x][t%T[x]];
      if(w<=del){
        x=y;
        t+=w;
        del-=w;
      }
      if(g[i][x][t%T[x]]<=del) i++;
    }
    if(del){
      x=c[x][t%T[x]];
      t++;
      del--;
    }
  }
  return x;
}

void solve(){
  scanf("%d%d",&n,&q);
  T.resize(n+1);
  L(i,1,n,1){
    scanf("%d",&T[i]);
    maxT=max(maxT,T[i]);
  }
  L(i,1,n,1){
    c[i].resize(T[i]);
    L(j,0,T[i]-1,1){
      scanf("%d",&c[i][j]);
    }
    L(j,0,LOG-1,1){
      f[j][i].resize(T[i]);
      g[j][i].resize(T[i]);
    }
  }
  build();
  while(q--){
    int x;
    i64 t,del;
    scanf("%d%lld%lld",&x,&t,&del);
    printf("%d\n",query(x,t,del));
  }
}
posted @ 2026-08-22 19:33  jess1ca1o0g3  阅读(2)  评论(0)    收藏  举报