Solução por Rogério Júnior
Para ver o problema original, clique aqui. Esta foi uma das tarefas da Seletiva Brasileira para Olimpíada Internacional de Informática de 2016.
O problema é uma simples, basta encontrarmos a recursão. Vamos construir o serrote a partir de outros serrotes de tamanho , adicionando o número (note que ele é maior que todos os outros números no serrote, logo sempre irá formar um dente se tiver vizinhos). Temos duas possibilidades: ou adicionamos o valor em um serrote de maneira a criar um novo dente ou inserimos em um serrote de modo que nenhum novo dente seja criado.
Para não criarmos um novo dente, adicionamos o novo valor nas pontas ou dentro de algum dente já existente. Se , e formam um dente, por exemplo, então adicionar um valor maior que os três entre e ou entre e não irá alterar a quantidade de dentes, pois iremos destruir o dente anterior para criar um novo. Em um serrote com dentes há lugares em quen podemos inserir um novo ponto dentro de um dente já existente, mais as duas pontas, logo há possibilidades para inserir um ponto sem alterar a quantidade dos dentes. Como há maneiras de construir um serrote de pontos e dentes, há maneiras de construir um serrote a partir de um .
Vejamos agora como construir a partir de um serrote de tamanho , com dentes, adicionando um ponto que cria um novo dente. Se há maneiras de inserir um ponto em uma dada permutação ( maneiras de inserir entre dos pontos e maneiras de inserir nas pontas) e sabemos que, com dentes, há maneiras de inserirmos sem alterarmos a quantidade de dentes, então há maneiras de inserirmos de modo a aumentarmos a quantidade de dentes. Como há maneiras de construir um serrote de pontos e dentes, há maneiras de construir um serrote a partir de um .
Somando as duas possibilidades de construção, temos que . Basta agora escrevermos o caso base da recursão, que é bem simples. Para , temos maneiras de construir um serrote sem dentes (), que são , , e e duas maneiras de construirmos um serrote com um dente (), que são e . Como estas são todas as permutações, então não há nenhuma maneira de construirmos um serrote de tamanho com alguma outra quantidade de dentes ( e ).
Basta implementarmos a função recursiva descrita acima, usando uma tabela de para evitarmos o recálculo. Segue o código para melhor entendimento.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Serrote - Dia 1 - Seletiva IOI 2016 | |
// Rogério Júnior | |
// Complexidade: O(n*k) | |
#include <cstdio> // scanf e printf | |
#include <cstring> // memset | |
#define MOD 1000000007LL // defino o valor de MOD | |
#define MAXN 1010 // defino o valor de MAXN | |
// defino que ll denominará o tipo long long | |
typedef long long ll; | |
// tabela de DP, para evitar recálculo | |
ll dp[MAXN][MAXN]; | |
// função recursiva que chama a DP Top-Down | |
ll solve(ll n, ll k){ | |
// se já tenho o resultado salvo, retorno o que tenho | |
if(dp[n][k]>=0) return dp[n][k]; | |
// caso base: se n=3 | |
if(n==3){ | |
// se k=0, há 4 possibilidades | |
if(k==0) return dp[n][k]=4; | |
// se k=1, há uma possibilidade | |
if(k==1) return dp[n][k]=2; | |
// para qualquer outro k, não há possibilidades | |
return dp[n][k]=0; | |
} | |
// se n>3, então chame a recursão geral da DP, olhando como construir | |
// o serrote de tamanho n a partir de serrotes de tamanho n-1 | |
// e não podemos esquecer de retornar apenas o módulo (10^9+7) | |
return dp[n][k]=(solve(n-1,k-1)*(n-2*k)+solve(n-1,k)*2*(k+1))%MOD; | |
} | |
int main(){ | |
// marco todos os valores da DP como não calculados | |
memset(dp,-1,nof dp); | |
// declaro e leio os valores de n e k | |
ll n, k; | |
scanf("%lld %lld", &n, &k); | |
// e imprimo a resposta para o n e o k fornecidos | |
printf("%lld\n", solve(n,k)); | |
return 0; | |
} |