Introduo Neste texto sero apresentadas dicas para as solues dos problemas da prova da sub-regional brasileira da maratona de programao de 2013. Alm da discusso dos algoritmos esperados, o texto contm dicas de implementao em C++, links teis para estudo e referncias a problemas de outras competies que so relacionados aos temas das questes da prova. O enunciado das questes da prova pode ser obtido no seguinte link: http://maratona.ime.usp.br/prim-fase2013/maratona.pdf
Problema A Zerinho ou Um Soluo Esse era o problema mais fcil da prova. Ele pode ser resolvido com a implementao de uma lgica simples. A seguir, um exemplo de uma implementao curta para o problema:
Links para estudo http://www.cplusplus.com/doc/tutorial/control/
Problema B Balo Soluo O problema nos d N segmentos e tambm C pontos de partida para bales que iram subir e deslizar nos segmentos. Para cada balo temos de dizer qual o seu destino. Os valores de N e C podem ir at 5 10 , ento j sabemos que teremos de ser bem eficientes para descobrir as respostas das consultas, o programa no passar no tempo se tentarmos simular o caminho do balo para cada consulta. Para a resoluo do problema crucial descobrir para cada consulta qual o primeiro segmento que o balo encosta (ou se ele escapa direto). Tambm importante saber para cada segmento o que acontece com o balo depois de tocar esse segmento, para qual segmento o balo vai (ou se ele escapa). Nessa hora vamos precisar de um algoritmo baseado em line sweep. Imagine uma reta vertical passando no plano do problema, vindo desde o menor valor de x e indo at o maior valor. Acontecem 3 tipos de eventos interessantes: - A reta vertical encosta em um segmento. - A reta vertical desencosta de um segmento. - A reta vertical chega em um valor de x de onde um balo solto. Se conseguirmos manter o conjunto de segmentos que toca essa reta em um determinado instante durante esse processo, isso pode nos ajudar a descobrir o destino do balo aps ser solto e aps tocar em um segmento. Alm de simplesmente manter esse conjunto, ser interessante manter esse conjunto de segmentos ordenado, do segmento que est mais baixo at o segmento mais alto. A implementao disso no to simples, por isso esse texto tentar mostrar alguns detalhes de como fazer isso utilizando C++. Iremos precisar de estruturas para ponto e segmento. A estrutura de ponto deve possuir dois inteiros, x e y . A estrutura de segmento deve possuir dois pontos, 1 p e 2 p tal que 1. 2. p x p x < . Para manter o conjunto de segmentos ordenados de baixo para cima, usaremos o set da STL do C++. Iremos criar nossa prpria funo de ordenao para o set receber os identificadores dos segmentos e orden- los para que os segmentos fiquem de baixo para cima no conjunto. Veja uma possvel implementao desse set:
Com essa estrutura de set, pode-se ento jogar os identificadores dos segmentos no conjunto e eles ficaro ordenados de acordo com a altura dos segmentos. Agora vamos ver como lidar com os trs tipos de evento: - 1) Reta vertical encosta em um segmento de nmero k : o Insira k no conjunto. o Se [ ]. 1. [ ]. 2. v k p y v k p y > , aps um balo tocar o segmento k , ele ir escapar para o segmento logo acima de k no conjunto. - 2) Reta vertical desencosta de um segmento de nmero k : o Se [ ]. 2. [ ]. 1. v k p y v k p y > , aps um balo tocar o segmento k , ele ir escapar para o segmento logo acima de k no conjunto. o Remover k do conjunto. - 3) Reta vertical chega ao x de uma consulta: o Se o conjunto de segmentos estiver vazio, o balo escapa direto nessa consulta. o Caso contrrio, o primeiro segmento que o balo encostar o primeiro segmento presente no conjunto, * . () lines begin . Note que para isso funcionar, se houver empate de coordenada x entre os eventos, o evento tipo 1 deve preceder o evento tipo 3, e o evento tipo 3 deve preceder o tipo 2. O enunciado deixa explcito que no h x em comum entre dois segmentos, ento no h empate entre eventos do tipo 1 e 2. Se houvesse x em comum seria necessria uma travessia um pouco mais complicada nos eventos. Outro detalhe como pegar um segmento que est no conjunto e descobrir o segmento acima dele. Isso pode ser feito da seguinte maneira:
Agora que temos as informaes sobre os destinos dos bales aps baterem nos segmentos e serem soltos nas consultas, podemos utilizar uma programao dinmica que dado um segmento inclinado, responde qual seria o destino final do balo. A parte mais difcil j foi, o leitor fica encarregado dos detalhes de implementao dessa parte final da resoluo.
Links para estudo http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lineSweep
Problemas relacionados http://www.spoj.com/problems/RAIN1/ Problema C Chefe Soluo dado um grafo direcionado acclico e pede-se um programa que realize instrues de trocas de vrtices e perguntas sobre antecessores diretos ou indiretos de um vrtice. Vamos discutir primeiro a instruo de troca de vrtices. Quando se realizado uma troca de vrtices no grafo, a estrutura do grafo no muda. Durante a execuo do programa, s precisamos saber onde est cada pessoa no grafo e qual a idade de cada vrtice. Podemos manter dois vetores, [ ] pos i igual ao vrtice atual da pessoa i e [ ] idade i sendo a idade da pessoa no vrtice i . Dessa maneira, a troca de duas pessoas a e b pode ser feito em tempo (1) O como mostrado abaixo:
Inicialmente, pode-se fazer [ ] pos i i = para todo i . Agora sero discutidas duas solues para a outra instruo. Soluo 1 Para implementar a instruo de pergunta, pode-se realizar uma busca para cada instruo. Isso pode ser feito usando busca em largura (BFS) ou busca em profundidade (DFS) atravessando as arestas no sentido contrrio. A complexidade para cada query ser de ( ) O N M + . Soluo 2 Como j foi dito, as operaes de troca de vrtices no alteram a estrutura do grafo. Com isso, podemos pr-calcular todos os antecessores dos vrtices do grafo. A relao de antecessor transitiva, se A antecessor de B e B antecessor de C, ento A antecessor de C. Para achar todos os antecessores de um vrtice, precisamos calcular o fecho transitivo da relao (ver transitive closure nos links para estudo). Para calcular o fecho transitivo pode-se utilizar o algoritmo de Floyd-Warshall. Inicialmente, caso exista aresta de A para B , faz-se [ ][ ] 1 antecessor A B = , caso contrrio [ ][ ] 0 antecessor A B = . Aps isso, utiliza-se o Floyd-Warshall como mostrado abaixo:
Aps executar esse trecho de cdigo, [ ][ ] antecessor A B igual a 1 se e somente se A antecessor de B . Possuindo essa tabela, cada instruo de pergunta pode ser feita em ( ) O N iterando-se por todos os vrtices e verificando quais deles so antecessores do vrtice perguntado.
Links para estudo http://en.wikipedia.org/wiki/Depth-first_search http://en.wikipedia.org/wiki/Breadth-first_search http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=graphsDataStrucs1 http://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm http://en.wikipedia.org/wiki/Transitive_closure
Problemas relacionados http://br.spoj.com/problems/DEPENDEN/ Problema D Mquina dobradora Soluo dado uma fita de N nmeros em um estado inicial. O problema nos pede para dizer se aps algumas dobras possvel se chegar em um estado final. Deve-se atentar ao fato que o limite para N igual a 15. Esse valor baixo nos sugere que esse problema pode ser resolvido com fora bruta, testando todas as formas de dobrar recursivamente. possvel se calcular com uma funo recursiva que o nmero de maneiras de dobrar uma fita de tamanho 15 na ordem de um milho. Dependendo da eficincia do programa para calcular todas essas dobras, talvez seja necessrio algum corte na fora bruta, como por exemplo: - Se a soma dos nmeros da fita inicial for diferente da soma da fita final, a resposta N. - No adianta dobrar mais vezes a fita se ela j est menor que o estado final. - Aps uma dobra, os nmeros s podem aumentar. Se o elemento mximo da fita passar do elemento mximo do estado final, tambm se pode realizar um corte na busca.
Links para estudo http://en.wikipedia.org/wiki/Brute-force_search
Problemas relacionados http://www.urionlinejudge.com.br/judge/problems/view/1395 Problema E Mergulho Soluo O problema pedia para achar quais nmeros inteiros de 1 a N no apareciam na entrada. O problema poderia ser resolvido utilizando um vetor com N posies para marcar os nmeros que apareceram na entrada. Se o nmero i aparece na entrada, pode-se fazer [ ] 1 v i = . Dessa maneira possvel obter uma soluo com complexidade de tempo ( ) O N . possvel tambm utilizar a estrutura do set da STL do C++ para obter uma soluo na complexidade ( .log ) O N N , pois o set possui funes de insero e busca em tempo (log ) O N .
Links para estudo http://www.cplusplus.com/doc/tutorial/arrays/ http://www.cplusplus.com/reference/set/set/
Problemas relacionados Pea perdida OBI 2007 Programao nvel 1 fase 1 http://olimpiada.ic.unicamp.br/passadas/OBI2007/res_fase1_prog/programacao_n1/tarefas_s olucoes
Problema F Tringulos Soluo 1 So dados 5 10 N s pontos em uma circunferncia e pede-se quantos tringulos equilteros possvel formar com esses pontos. Primeiro fato que temos de notar o limite para o N . No podemos, por exemplo, testar todos os trios de pontos ou todos os pares de pontos, nesse caso o programa exceder o tempo limite. Uma informao valiosa nesse problema que o tringulo equiltero determina 3 arcos iguais no crculo, ou seja, podemos tomar a soma de todos os arcos e dividir por 3 para achar o tamanho do arco que os pontos de tringulo equiltero determinam. Se a soma no for divisvel por 3, a resposta para o problema nula. Seja o vetor [] soma definido por 1 [ ] i i k soma i X = =
, ou seja, o vetor soma
representa a soma acumulada dos arcos da entrada. Para encontrar um tringulo equiltero, devemos encontrar os trios i , j , k tais que: [ ] [ ] ( [ ] / 3) [ ] [ ] 2*( [ ] / 3) soma j soma i soma N soma k soma i soma N = + = +
Podemos testar todos os valores possveis i , so N possibilidades. Para achar j e k podemos utilizar busca binria ou a estrutura de set da STL, resultando em uma soluo com complexidade de tempo ( .log ) O N N . Soluo 2 Podemos melhorar um pouco o ltimo passo da soluo 1 e conseguir um algoritmo que roda em tempo linear. A medida que tentamos as possibilidades para o i em ordem crescente, possvel fazer a busca por j e k apenas incrementando seus valores.
Veja a seguir um trecho de um cdigo que mostra como implementar isso:
Perceba que apesar dos loops aninhados, esse algoritmo linear no tempo, pois as variveis i , j , k so incrementadas no mximo N vezes.
Links para estudo http://www.cplusplus.com/reference/set/set/ http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch
Problemas relacionados http://br.spoj.com/problems/POLIGONO/ Problema G Linhas de contineres Soluo 1 dada uma matriz LxC com elementos embaralhados e pede-se o nmero mnimo de movimentos de troca de linhas ou colunas para desembaralhar a matriz. A primeira soluo que ser apresentada construtiva, descreve como realizar vrios movimentos necessrios at que se descubra que a matriz est desembaralhada ou que a tarefa impossvel. Primeira coisa que podemos achar o elemento igual a 1 e trocar sua coluna com a primeira coluna e sua linha pela primeira linha. A partir disso, no ser mais possvel mexer na primeira linha e nem na primeira coluna. A primeira linha deve conter os nmeros de 1 a C e a primeira coluna deve conter todos os nmeros que possuem resto 1 por C . Caso contrrio, no haver soluo para o teste. Agora vamos arrumar a primeira linha e a primeira coluna. Enquanto houver elementos em posies erradas na primeira linha, troque duas colunas para colocar pelo menos um elemento a mais em sua posio correta. Faa o mesmo com a primeira coluna, enquanto houver elementos errados troque linhas. Aps isso, se a matriz toda ficar na posio correta, imprima o nmero de passos que foram feitos. Caso contrrio no h soluo, pois qualquer troca de coluna ir desarrumar a primeira linha e qualquer troca de linha ir desarrumar a primeira coluna. O nmero mximo para a resposta 2 L C + e cada operao de troca de fileira pode ser feita em tempo linear no nmero de elementos movidos. Dessa maneira, essa soluo possui complexidade ( ) O LC .
Soluo 2 A segunda soluo que ser apresentada no faz nenhuma alterao na matriz para descobrir a resposta. Analisaremos as permutaes presentes na matriz e a soluo ser encontrada a partir da contagem de ciclos de permutao (ver links para estudo). Se dois elementos esto na mesma coluna, eles sempre ficaro na mesma coluna aps as operaes de troca de fileiras. O mesmo acontece com dois elementos que esto na mesma linhaa, eles sempre estaro na mesma linha. Se verificarmos que todos os elementos [ ][ ] X i j pertencem mesma linha do elemento [ ][ 1] X i j e mesma coluna do elemento [ 1][ ] X i j , ento h resposta para o problema. O valor da resposta ser dado pela soma L C + menos o nmero de ciclos de permutao de uma linha e de uma coluna (todas as linhas equivalem mesma permutao, o mesmo ocorre para as colunas). A soluo tambm pode ser implementada na mesma complexidade da soluo 1.
Links para estudo http://mathworld.wolfram.com/PermutationCycle.html
Problemas relacionados http://br.spoj.com/problems/CADEIR09/ Problema H nibus Soluo O problema pede a contagem de maneiras que se pode formar uma fila com dois tipos de nibus. Existem K nibus de tamanho 5 e L nibus de tamanho 10. Como N mltiplo de 5, podemos comear o problema dividindo N por 5 e considerar que os nibus tem tamanho 1 e 2. O prximo passo da soluo obter uma formula recursiva para o problema. Seja ( ) f n o nmero de maneiras de formar uma fila de tamanho n . Tem-se (0) 1 f = (uma maneira de formar uma fila vazia) e (1) f K = (s d para usar um dos K nibus de tamanho 1). Para 2 n > , pode-se comear a fila por um nibus de tamanho 1 ou 2. Se escolhermos o comeo pelo nibus de tamanho 1, h K possibilidades para escolher o tipo do nibus e h ( 1) f n possibilidades para escolher o resto da fila, resultando em . ( 1) K f n maneiras. De modo similar, para o comeo com nibus de tamanho 2 haver . ( 2) L f n maneiras. Com isso, ( ) . ( 1) . ( 2) f n K f n L f n = + para 2 n > . Descoberta a recorrncia, poderamos utilizar programao dinmica para obter uma soluo na complexidade de tempo ( ) O N . Porm o valor de N pode ser muito alto, isso no seria suficiente para passar no tempo. A recorrncia encontrada linear, assim como a sequncia de Fibonacci. Vamos aprender agora como calcular ( ) f n rapidamente utilizando exponenciao de matrizes. Repare que a seguinte identidade vlida: | | | | 0 ( 2) ( 1) . ( 1) ( ) 1 L f n f n f n f n K ( = (
Com essa frmula, pode-se chegar no valor de ( ) f n a partir dos valores de (0) f e (1) f da seguinte maneira: | | | | | | | | | | | | | | 2 0 (0) (1) . (1) (2) 1 0 0 (0) (1) . (1) (2) . (2) (3) 1 1 0 (0) (1) . ( ) ( 1) 1 n L f f f f K L L f f f f f f K K L f f f n f n K ( = (
( ( = = ( (
( = + (
Para obter a resposta do problema, basta agora que tenhamos um mtodo rpido para fazer exponenciao de matrizes. Existe um mtodo rpido para isso, veja o link para estudo sobre exponentiation by squaring. Segue abaixo um pseudo cdigo que mostra como fazer a exponenciao com (log ) O N multiplicaes.
No se esqueam de sempre tirar resto por 6 10 nas operaes!
Links para estudo http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg http://en.wikipedia.org/wiki/Exponentiation_by_squaring Problemas relacionados http://www.urionlinejudge.com.br/judge/problems/view/1422 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=3 28&page=show_problem&problem=2304 Problema I Remendo Soluo So dadas N posies de furos em um pneu de bicicleta. Com remendos de tamanho 1 T e 2 T , quer se cobrir todos os furos utilizando o mnimo total de remendo. Um fato interessante para a busca da soluo tima que toda soluo pode ser transformada em um uma soluo onde todos os remendos cobrem um furo em uma de suas extremidades. Isso ocorre porque podemos transladar os remendos at que isso acontea e sem descobrir nenhum furo. Uma das complicaes do problema o fato do pneu ser circular. Vamos primeiro esquecer esse fato e fingir que o pneu uma tira esticada. Imagine o primeiro furo 1 F dessa tira. Esse furo dever ser coberto por um remendo de tamanho 1 T ou 2 T . Alm disso, podemos colocar os remendos com uma das pontas exatamente em 1 F . Aps colocar um remendo, podemos esquecer todos os pontos que ficaram cobertos. Dessa maneira, a soluo recai sobre o mesmo problema inicial, mas com um tira com menos furos. Quando isso acontece, pode-se tentar solucionar o problema com programao dinmica. Seja a funo ( ) cobertura i que retorna qual o mnimo de remendo para se cobrir os furos de 1 a i . Pode-se obter o seguinte pseudocdigo para essa funo:
Esse pseudocdigo no mostra o processo de memoizao, mas ele deve ser feito, necessrio salvar as respostas de ( ) cobertura i para que a funo no calcule a mesma coisa vrias vezes. Com isso resolvemos o problema para uma tira, no para o pneu. Mas o pneu tem N possibilidades para ser transformado em uma tira, pode-se cortar o pneu entre os N furos. Deve-se testar todas as tiras e pegar a menor resposta. Como a funo cobertura tem complexidade ( ) O N , pode- se obter a resposta para todas as tira em 2 ( ) O N . Outro detalhe que os valores de j que a funo cobertura usa podem ser pr-calculados em 2 ( ) O N , ( .log ) O N N ou ( ) O N utilizando loops ou usando tcnicas que foram usadas nas solues do problema F Tringulos.
Links para estudo http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=dynProg Problemas relacionados https://icpcarchive.ecs.baylor.edu/index.php?option=onlinejudge&page=show_problem&prob lem=1152 Problema J Caminho Soluo 1 O problema nos d um grafo de N vrtices e M arestas. Nesse grafo necessrio obter a resposta de S consultas. Escolhidas duas cidades, deve-se imprimir o valor mximo que pode ter a menor aresta de um caminho entre as duas cidades. possvel modificar a funo de distncia dos algoritmos de Dijkstra ou Floyd-Warshall para eles obterem as respostas das consultas do problema. Utilizando o algoritmo de Dijkstra, seria necessrio execut-lo para cada consulta, obtendo soluo na complexidade ( .( ).log ) O S N M N + implementando o algoritmo com fila de prioridade. Usando o Floyd- Warshall, possvel pr-processar todas as consultas, a complexidade seria 3 ( ) O N S + . Nenhuma dessas tentativas passa no tempo. Uma alternativa seria tentar resolver o problema por uma busca linear na resposta. Para cada consulta entre as cidades A e B , comeasse um grafo sem arestas. A cada passo, adiciona-se ao grafo a maior aresta que ainda no foi colocada. Quando existir caminho entre A e B , a resposta para a consulta dada pelo valor da ltima aresta adicionada. A existncia do caminho poderia ser verificada utilizando algoritmo de union-find. Isso possibilita a resoluo em complexidade ( . ) O S M , que ainda no suficiente para passar. Analisando melhor essa ltima resoluo, no precisamos adicionar as arestas ao grafo que no liguem vrtices em componentes conexas diferentes. Dessa maneira, o algoritmo executado seria idntico ao algoritmo de Kruskal para achar a rvore geradora mxima do grafo. Isso nos leva a um fato muito importante para a resoluo do problema: as respostas de todas as consultas permanecem as mesmas se analisarmos apenas a rvore geradora mxima do grafo. Ou seja, inicialmente podemos obter essa rvore e pensar agora como responder as consultas para a rvore. A rvore geradora mxima normalmente obtida pelo algoritmo de Kruskal ou pelo algoritmo de Prim. No caso da soluo que ser discutida agora, o uso do algoritmo de Prim ser mais prtico, pois ele encontra a rvore geradora j determinando um pai para cada n. Com o Kruskal, necessrio utilizar uma busca como a DFS para a obteno dos pais. Falta pensar como proceder as consultas na rvore. A grande vantagem da rvore que s h um caminho entre um par de vrtices. Para cada consulta necessrio achar esse caminho e dizer a menor aresta. Isso pode ser feito com uma busca para cada consulta, o que leva a uma complexidade de ( . ) O S N , que ainda no passa no tempo. Para encontrar rapidamente o caminho entre dois vrtices numa rvore pode-se utilizar a tcnica de encontrar o primeiro ancestral comum entre dois vrtices em tempo logartmico. Essa tcnica explicada no artigo do site do Topcoder apresentado na seo de links para estudo. Mesmo assim, esse texto explicar a implementao da aplicao dessa tcnica especificamente para encontrar a menor aresta em um caminho na rvore. Aps encontrar a rvore geradora mxima, deve-se encontrar a distncia de cada vrtice at a raiz, ou seja, a altura de cada vrtice na rvore. Pode-se escolher qualquer vrtice como raiz, sua altura ser igual 0. Deve- se calcular tambm uma tabela [ ][ ] t i j com dois valores. Seja o caminho formado por 2 j arestas que comea no vrtice i e vai em direo raiz. O vrtice final do caminho ser igual a [ ][ ]. t i j pai e a menor aresta do caminho ser [ ][ ]. t i j val . Os valores de [ ][0] t i so calculados facilmente a partir da construo da rvore. O cdigo a seguir mostra como calcular o resto da tabela com complexidade de tempo constante para cada elemento.
Com a tabela pronta, possvel utiliz-la nas consultas para obter uma complexidade logartmica para achar a menor aresta entre dois vrtices. Sendo logn o maior nmero tal que (1 log ) n n << < , veja o cdigo a seguir que encontra a menor aresta entre dois vrtices A e B .
Ao final da funo, as variveis a e b sempre acabam iguais ao primeiro ancestral comum dos dois vrtices passados para a funo, mas nessa questo a identidade do ancestral no precisou ser utilizada. Ao final do clculo da arvore geradora mxima, do clculo da tabela e do clculo das respostas das consultas, obtemos um algoritmo com complexidade de tempo igual a ( .log .log .log ) O M M N N S N + + , que suficiente para passar no tempo limite do problema. Soluo 2 A segunda soluo que ser discutida agora leva em conta o fato de que no precisamos responder cada consulta no mesmo momento em que fazemos leitura da mesma na entrada. Por exemplo, pode-se fazer a leitura de todas as consultas e s aps isso responder todas as consultas na ordem dada na entrada. Ser proposta aqui uma maneira de se modificar o algoritmo de union-find durante a execuo do algoritmo de Kruskal para que ao final da execuo todas as consultas j estejam respondidas. Durante a execuo do algoritmo de Kruskal, manteremos para cada componente conexa um conjunto de identificadores de consultas. Inicialmente, o grafo comea sem arestas, cada vrtice representa uma componente diferente e sendo assim cada um possui um conjunto de identificadores. Aps a leitura de todas as consultas, se a consulta de nmero i dada pelos vrtices a e b , adiciona-se o inteiro i aos conjuntos dos vrtices a e b . dessa maneira ento que iremos comear o nosso algoritmo, vamos analisar agora como fazer a juno das componentes e responder as consultas. O algoritmo de Kruskal ir adicionando ordenadamente as arestas no grafo, da maior para a menor. As arestas que realmente sero inseridas juntam duas componentes conexas diferentes. Quando essa juno ocorrer, iremos juntar tambm o conjunto de identificadores dessas componentes. Se uma aresta juntar duas componentes que contm um mesmo identificador de consulta, a resposta dessa consulta ser igual ao valor da aresta. Com isso em mente, veja a seguir o pseudocdigo da modificao do algoritmo de union-find para resolver o problema.
Aps usar a funo join para todas as arestas do grafo, indo da maior para a menor, todas as consultas estaro respondidas no vetor resp . Agora falta analisar a complexidade do algoritmo. Os conjuntos de identificadores s precisam permitir as operaes de insero, travessia por todo o conjunto e deleo de todo o conjunto. Usando a estrutura de vector da STL ou usando uma lista ligada, a insero pode ser feita em tempo constante e as outras duas operaes podem ser feitas em tempo linear no nmero de elementos do conjunto. Em uma anlise ingnua, poder-se-ia dizer que a complexidade da funo join de ( ) O S , levando o algoritmo ter complexidade ( . ) O N S , porm temos de analisar melhor quantas operaes pode-se ter no total. Toda vez que um identificador muda de conjunto, ele vai para um conjunto maior ou de mesmo tamanho que o conjunto em que ele estava. Aps a passagem de todos os elementos do conjunto de j para o conjunto de i , os elementos que estavam no conjunto de j passam a estar num conjunto que pelo menos duas vezes maior que o conjunto onde eles estavam. Isso significa que cada identificador de consulta movido no mximo 2 log (2 ) S vezes, pois nenhum conjunto pode ter tamanho maior que 2.S . Aps essa anlise, descobrimos que o algoritmo pode ser executado em complexidade de tempo igual a ( .log .log ) O M M S S + , o que inclui a ordenao das arestas e a execuo do algoritmo de Kruskal com o union- find modificado. A vantagem da soluo 2 que sua implementao pode ficar bem menor que a implementao da soluo 1, podendo ter por volta de 80 linhas de cdigo em C++. Um detalhe de implementao que pode ser destacado que durante a execuo da funo join , pode-se descartar identificadores de consultas j respondidas para diminuir o tempo mdio de execuo.
Links para estudo http://pt.wikipedia.org/wiki/Algoritmo_de_Prim http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncesto r#Lowest Common Ancestor (LCA) http://pt.wikipedia.org/wiki/Algoritmo_de_Kruskal http://en.wikipedia.org/wiki/Disjoint-set_data_structure Problemas relacionados https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show _problem&problem=2297 http://br.spoj.com/problems/ANTS10/ http://www.spoj.com/problems/KOICOST/