https://leetcode-cn.com/problems/first-day-where-you-have-been-in-all-the-rooms/
思路:经典读错题+想歪。分析后不难发现,第一次到达房间
i
i
i,下一次必定要回到
<
=
i
<=i
<=i的房间
j
j
j(不妨把这个操作叫做回访),且此时
<
i
<i
<i的房间都到达了偶数次,那么对于
[
j
,
i
)
[j,i)
[j,i)的每个房间,我们都需要再次回访,如果用
d
p
i
dp_i
dpi表示在位置
i
i
i进行回访操作后再重新回到位置
i
i
i所需要的天数,那么有:
d
p
i
=
i
−
j
+
1
+
∑
k
=
j
i
−
1
d
p
k
dp_i=i-j+1+\sum_{k=j}^{i-1}dp_k
dpi=i−j+1+k=j∑i−1dpk
显然最终结果就等于
∑
i
=
0
n
−
2
d
p
i
+
n
−
1
\sum_{i=0}^{n-2}dp_i+n-1
∑i=0n−2dpi+n−1。但是这样转移复杂度是
O
(
n
2
)
O(n^2)
O(n2)的。那么考虑记录
d
p
dp
dp的前缀和
s
s
s,则转移方程转换为:
s
i
=
s
i
−
1
+
d
p
i
=
s
i
−
1
+
s
i
−
1
−
s
j
−
1
+
i
−
j
+
1
s_i=s_{i-1}+dp_i=s_{i-1}+s_{i-1}-s_{j-1}+i-j+1
si=si−1+dpi=si−1+si−1−sj−1+i−j+1
最终结果等于
s
n
−
2
+
n
−
1
s_{n-2}+n-1
sn−2+n−1,这样复杂度就优化到了
O
(
n
)
O(n)
O(n)。
class Solution {
public:
int firstDayBeenInAllRooms(vector<int>& nextVisit) {
int n=nextVisit.size();
using ll=long long;
const int mod=1e9+7;
vector<ll> sum(n-1);
sum[0]=1;
for(int i=1;i<n-1;i++)
{
ll val=0;
if(nextVisit[i]>0)
val=sum[nextVisit[i]-1];
sum[i]=(sum[i-1]+sum[i-1]-val+i-nextVisit[i]+1+mod)%mod;
}
return (sum[n-2]+n-1)%mod;
}
};
等价的代码:
class Solution {
public:
int firstDayBeenInAllRooms(vector<int>& nextVisit) {
int n=nextVisit.size();
using ll=long long;
const int mod=1e9+7;
vector<ll> sum(n);
for(int i=0;i<n-1;i++)
sum[i+1]=(sum[i]+sum[i]-sum[nextVisit[i]]+i-nextVisit[i]+1+mod)%mod;
return (sum[n-1]+n-1)%mod;
}
};