长路
题目描述
一天,小Vasya发现自己在一个有(n+1)个房间的迷宫里。刚开始,Vasya在第一个房间,为了走出迷宫,他必须到达第(n+1)个房间。
迷宫的构造是这样的:每个房间有两个单向传送门。考虑第i个房间,我们可以使用第一个传送门前往第i+1个房间,也可以使用第二个传送门前往第pi个房间,这里 1≤pi≤i。
为了防止迷路,Vasya决定按以下方式走出迷宫:
每次进入某房间,就在天花板上画一个X。刚开始,Vasya在第一个房间画一个X。
假设Vasya在第i个房间,而且已经画了X。然后,如果天花板上有奇数个X,Vasya会使用第二个传送门,否则使用第一个。
帮助Vasya计算他需要使用多少次传送门才能到达第n+1个房间。
数据范围
测试点 n
1~2 ≤5
3~5 ≤20
6~7 ≤100
8~15 ≤1000
16~20 ≤10^6
输入格式 2072.in
第一行一个整数n。 n ≥ 1。
第二行n个整数,第i个表示pi。
输出格式 2072.out
你的答案除以1 000 000 007的余数。
输入样例 2072.in
2
1 2
输出样例 2072.out
4
首先,想想纯模拟。也就是到达每一个点,判断X的奇偶,然后一步一步走。光看输出格式就可以知道,答案很大,超时是一定的。
得分:25。
下面进行优化。一步一步走太慢,那就一次走多步,进行递推。可以发现,到达一个点,都会经历这样的故事——启动二类防护门,从p[i]往i走一次;到达i后再启动一类防护门,到达i+1。很显然,p[i]往i走一次的步数可以在之前的计算中得到。由此,状态的定义、转移如下:
f[i]表示到达i后,直到到达i+1所需的步数。
f[i]=2+sigma(f[p[i]]+f[p[i]+1]+。。。。+f[i-1]);
时间复杂度O(N*N),得分75。
很显然,f[p[i]]+f[p[i]+1]+。。。+f[i-1]是连续的,因而可以用部分和进行优化。方程可简化为f[i]=2+sum[i-1]-sum[p[i-1]-1]。
不过,由于此处的f/sum都是取模后的结果,因此sum[i-1]和sum[p[i-1]]的大小关系不确定,造成答案的错误。根据模的原理,if(sum[i-1]< sum[p[i-1]])f[i]=(2+(mod+sum[i-1]-sum[p[i]-1])%mod)%mod;
最后输出sum[n]即可。
时间复杂度:O(N)得分100。
PS:看到时间复杂度大的题目,不要当即头大。考试时就要推一推,算一算。这题真心不难……
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=1e6+5;
long long n;
int p[MAXN];
long long f[MAXN],sum[MAXN];
long long mod=1000000007;
int main()
{
cin>>n;
for(inti=1;i<=n;i++) cin>>p[i];
f[1]=2;
sum[1]=2;
for(inti=2;i<=n;i++)
{
if(sum[i-1]>sum[p[i]-1]) f[i]=(2+sum[i-1]-sum[p[i]-1])%mod;
else f[i]=(2+(mod+sum[i-1]-sum[p[i]-1])%mod)%mod;
sum[i]=(sum[i-1]+f[i])%mod;
}
cout<<sum[n]<<endl;
return0;
}