//Kterink!
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define NMAX 1024
#define MOD 9901
using namespace std;
int x[NMAX], sortedX[NMAX];
struct query
{
int num, val;
query ()
{
num = 0; val = 0;
}
} arb[4 * NMAX + 69]; //Acest numar ma excita serios
query arbQuery(int st, int dr, int nod, int wantedLeft, int wantedRight)
{
query ans, now;
if (wantedLeft > wantedRight)
return ans;
//Verific ca intervalul curent [st, dr] sa fie inclus in [wantedLeft, wantedRight]
//caz in care nu mai are rost sa merg mai departe, ma opresc si trimit toata solutia
if (wantedLeft <= st && dr <= wantedRight)
return arb[nod];
//Daca am intervale care se intersecteaza cu wantedLeft, wantedRight, o parte din solutia optima ar putea fi acolo
int med = (st + dr) / 2;
if (wantedLeft <= med)
{
now = arbQuery(st, med, 2 * nod, wantedLeft, wantedRight);
if (now.val == ans.val)
{
ans.num += now.val;
ans.num = ans.num % MOD;
}
if (now.val > ans.val)
ans.num = now.num, ans.val = now.val;
}
if (med < wantedRight)
{
now = arbQuery(med + 1, dr, 2 * nod + 1, wantedLeft, wantedRight);
if (now.val == ans.val)
{
ans.num += now.val;
ans.num = ans.num % MOD;
}
if (now.val > ans.val)
ans = now;
}
return ans;
}
void arbUpdate(int st, int dr, int nod, int wantedNod, query info)
{
if (st == dr) //Am ajuns la nodul cautat din arbore
{
arb[nod] = info;
return ;
}
int med = (st + dr) / 2;
int choice;
if (wantedNod <= med)
{
arbUpdate(st, med, 2 * nod, wantedNod, info);
choice = 2 * nod;
}
else
{
arbUpdate(med + 1, dr, 2 * nod + 1, wantedNod, info);
choice = 2 * nod + 1;
}
if (info.val == arb[nod].val)
{
arb[nod].num += info.num;
arb[nod].num %= MOD;
}
if (info.val > arb[nod].val)
arb[nod] = info;
}
int main()
{
int i, N;
freopen("subsiruri.in", "r", stdin);
freopen("subsiruri.out", "w", stdout);
//Citesc vectorul
scanf("%d", &N);
for (i = 1; i <= N; i ++)
scanf("%d", &x[i]);
//Brute force O(N ^ 2)
int dp[NMAX], how[NMAX], j;
memset(dp, 0, sizeof(dp));
memset(how, 0, sizeof(how));
int bestChoice, numChoices;
for (i = 1; i <= N; i ++)
{
bestChoice = numChoices = 0;
for (j = 1; j < i; j ++)
if (dp[j] > bestChoice && x[j] < x[i])
bestChoice = dp[j];
dp[i] = bestChoice + 1;
for (j = 1; j < i; j ++)
if (dp[j] == bestChoice && x[j] < x[i])
{
numChoices += how[j];
numChoices = numChoices % 9901;
}
if (numChoices == 0)
numChoices ++;
how[i] = numChoices;
}
bestChoice = 0; numChoices = 0;
for (i = 1; i <= N; i ++)
{
if (dp[i] == bestChoice)
numChoices += how[i];
if (dp[i] > bestChoice)
bestChoice = dp[i], numChoices = how[i];
}
printf("%d\n%d", bestChoice, numChoices);
return 0;
//Normalizez datele
for (i = 1; i <= N; i ++)
sortedX[i] = x[i];
sort(sortedX + 1, sortedX + N + 1);
int st, dr, med;
for (i = 1; i <= N; i ++)
{
st = 1; dr = N;
while (st <= dr)
{
med = (st + dr) / 2;
if (sortedX[med] == x[i])
{
x[i] = med;
break;
}
if (sortedX[med] < x[i])
st = med + 1;
else
dr = med - 1;
}
}
query now;
for (i = 1; i <= N; i ++)
{
//Un QUERY pe intervalul [i, j] imi da lungimea unei subsir crescator maximal si cate exista
//astfel incat ultimul element din subsir sa se afle in intervalul [i, j]
//La pasul curent un subsir crescator maximal care se termina in x[i] poate fi obtinut din
//orice alt subsir crescator maximal care se termina undeva in intervalul [1, x[i] - 1]
now = arbQuery(1, N, 1, 1, x[i] - 1);
now.val ++;
if (now.num == 0)
now.num = 1;
//Lungimea subsirului crescator maximal va creste cu 1 (am adaugat elementul x[i]), iar numarul va
//ramane acelasi (cel dat de queryul dinainte, deoarece se adauga un singur element la ce era inainte)
arbUpdate(1, N, 1, x[i], now);
}
//Afisez solutia finala
now = arbQuery(1, N, 1, 1, N);
printf("%d\n%d", now.val, now.num);
return 0;
}