i表示这个区间产生的最大数,j表示这个区间的左下标,f[i][j]表示这个区间的右下标。听不懂?没关系,先来看第一个for循环。
using namespace std;
int n, ans;
int f[N][M];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
int in;
scanf("%d", &in);
f[in][i] = i+1;
}
printf("%d", ans);
}
我们来看看题目给的例子吧
input output
4 31 1 1 2
按照它的输入我们会得到这样一组数据f[1][1]=2,f[1][2]=3,f[1][3]=4,f[2][4]=5.把它画成数轴会是怎样的呢。见下图。
在这里并没有按往常一样把初始数的区间表示为[1,1],[2,2]这种,其中一个原因是我们需要一个数轴,这也和我们后面的状态转移方程有关,先看一下这个状态转移方程f[i][j] = f[i - 1][f[i - 1][j]]。同理我们把它画成一个数轴看看这是什么意思。
在开头我就提了f[i][j]存储的是这个区间右端的下标,这个区间是一个能够合成i这个数的区间,同理f[i-1][j]表示[ j,f[i-1][j] ]的右端下标,这个区间里面是i-1这个数,举个例子吧假设我们要求f[2][3],根据区间状态方程f[2][3]=f[1][f[1][3]],我们知道f[1][3]=4那么f[2][3]的右下标是f[1][4],但是f[1][4]并不存在,所以能够合成2的左下标为3的区间不存在。但是如果把初始区间设置为f[1][1]=1,f[1][2]=2,f[1][3]=3,f[2][4]=4会怎样,f[2][1]=f[1][f[1][1]]=f[1][1]=1.这个时候[1,1]区间的值尽然自我合成变成了2?如果一直合成下去是不是只会产生58,按照[1,1]区间这样的分法,我们完全无法区分哪个数是左下标,哪个数是又下标,所以f[in][i] = i+1;的另一个目的是区分左右下标。(其实我也不太会解释,你只要知道我们需要一段来表示一个数而不是一个点就行了)。
到这里你是不是有点混乱了。我们来理一下思路
1.i表示这个区间的数(可能是初始的,也可能是合成的),j表示这个区间的左下标,f[i][j]表示这个区间的右下标。及[i,f[i][j]]区间。
2.f[i][j] = f[i - 1][f[i - 1][j]]这个状态方程的意义是给定一个左下标和一个值i,看看是否能找到两个值为i-1的“连续”区间,那么第二个区间的值就是f[i][j]的右下标。
#define N 61
#define M 270007
using namespace std;
int n, ans;
int f[N][M];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
{
int in;
scanf("%d", &in);
f[in][i] = i+1;
}
for (int i = 2; i <= 58; ++i)//58?????
for (int j = 1; j <= n; ++j)
{
if (!f[i][j])
f[i][j] = f[i - 1][f[i - 1][j]];
if (f[i][j])
ans = i;
}
printf("%d", ans);
}
先看数据量2<=N<=262144=2^18,每个正整数大于等于1小于等于40,两个40可以合成41,四个两个41可以合成42,两个42可以合成43这时已经消耗了2^3个40,以此类推2^18次方个58,这时最大的情况。同时i表示这个区间合成的数,同时最小的情况,两个1可以合成2,那么2<=i<=58.
j表示左下标,它小于n这很容易理解,当f[i][j]为0的时候说明这个区间要么为0要么还没有被合成过,如果不为0让ans=i,因为i只会变大,只要它不为0那就记录它,ans也会随着不为0的区间变大。只要把状态转移方程放在if里就行了,最后打印ans。