0%

[USACO24FEB] Infinite Adventure P

神奇的倍增题,场上多个 草过 分。

题目链接

题意

现在有 个城市,每一个城市有一个传送门。

个城市的传送门以 为周期,如果第 天进入第 个城市的传送门,你就会被传送到第 个城市。

组询问,每一次给出 。你需要于第 天进入第 个城市的传送门,并等待一天,继续进入所在城市的传送门……

求进入 次后,求所在的城市编号。

所有的 均为 的幂。

题解

这道题中,每一次询问都需要走 步,那大概率就是倍增之类的东西,考虑往倍增上想。

考虑 表示第 个时刻从城市 出发,走 到的城市。

但这个做法有一个问题。

假如我们中途走到了一个城市 满足 ,那么这个时候我们的状态就不够用了。

那么我们就只处理所有城市都不大于 的部分,并且额外计算,走了多久到达第一个 的点

尝试计算复杂度。

对于每一次询问,要么我们直接跳到下一个 更大的点,在 的时间内将 翻倍。

否则可以 直接往前走最大的 步,但是这样可能会到达一个 更小的点。

单组询问复杂度为 ,但是预处理的时候需要询问 次。

总复杂度为 ,可以过前 个点,但是还差 个点。

发现时间复杂度瓶颈主要在于,我们需要频繁在不同的 之间切换。

定义:称两个点 同层当且仅当

不妨直接令 表示,第 个时刻从城市 出发,经过的第 个同层城市是哪一个,同时记录 表示经过的时间。

此外,还需要计算 到达的第一个更高层点。

再次尝试计算复杂度。

对于预处理,我们需要优先计算 更小的城市。

对于点 与时间 ,我们的预处理分为两步,第一步是计算到达的第一个同层点,第二步是倍增。

第二步是简单的,直接倍增即可,复杂度是

考虑如何计算第一个同层点,首先我们先从 出发往前走一步到达每一个点

之后,由于低层点已经计算完毕了,每一次都只需要 的时间,直接从 跳到下一个比 层数更高的点

这样,我们至多跳 次就可以跳到第一个层数 的点。

故预处理的总复杂度是

对于一组询问来说,我们会先花 的时间走到整条路径的最高点,然后的再花 的时间将最高层走完。

那么之后走到的点,高度一定比当前点小,再走一步,然后重复上面的过程即可。

因为层数只有 种,所以最大高度只会变化 次。

故总时间复杂度为

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll lread()
{
ll s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
vc<int>num[100001];
ll tim[100001];int e[100001];
//经过tim秒,到了第一个高阶点e
int to[100001][70];
ll w[100001][70];
int s[100001];
int T[100001];
int n,m,cnt;
inline void init()
{
for(int lv=0;lv<=16;lv++)
{
vc<pi>nod;
for(int j=1;j<=n;j++) if(T[j]==(1<<lv)) for(int k=0;k<T[j];k++)
{
int id=s[j]+k;nod.push_back(pi(id,k));
ll t=1;int u=num[j][k];bool flag=0;
while(T[u]<T[j])
{
int nid=s[u]+((k+t)%T[u]);
if(tim[nid]==inf)
{
tim[id]=inf;
flag=1;
break;
}
t=min(t+tim[nid],inf);
u=e[nid];
}
if(!flag)
{
if(T[u]>T[j]) tim[id]=t,e[id]=u;
else to[id][0]=u,w[id][0]=t;
}
}

for(int i=1;i<=60;i++) for(auto p:nod)
{
int j=p.first,k=p.second;
if(!to[j][i-1]) continue;
ll t=w[j][i-1];int u=to[j][i-1];
int nid=s[u]+((t+k)%T[u]);

if(!to[nid][i-1]) tim[j]=min(inf,t+tim[nid]),e[j]=e[nid];
else w[j][i]=min(inf,t+w[nid][i-1]),to[j][i]=to[nid][i-1
}
for(auto p:nod)
{
int j=p.first;
if(!tim[j]) tim[j]=inf;
}
}
}
inline int get(int u,ll t,ll rest)
{
while(rest)
{
int id=s[u]+(t%T[u]);
if(rest>=tim[id])
{
t+=tim[id];
rest-=tim[id];
u=e[id];
}
else
{
for(int i=60;i>=0;i--) if(to[id][i]&&rest>=w[id][i])
{
rest-=w[id][i],t+=w[id][i],u=to[id][i];
id=s[u]+(t%T[u]);
}
if(rest)
{
u=num[u][t%T[u]];
rest--,t++;
}
}
}
return u;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) T[i]=read();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=T[i];j++) num[i].push_back(read());
s[i]=cnt+1,cnt+=T[i];
}
init();
for(int i=1;i<=m;i++)
{
int u=read();ll t=lread(),rest=lread();
printf("%d\n",get(u,t,rest));
}
return 0;
}

感谢观看!