Resolução dos Problemas D e F da Codeforces Round #694 (Div. 2)

Problema D: Definição Estranha

Definimos dois números como adjacentes se o resultado de lcm(a,b)/gcd(a,b) for um quadrado perfeito. Dada uma sequência de comprimento n, para cada elemento a[i], d[i] é a contagem de elementos adjacentes a ele. A cada segundo, cada elemento se transforma no produto dele mesmo com todos os seus elementos adjacentes. Para q consultas, dado x, devemos retornar o maior d[i] após x segundos.

A solução envolve fatorar os números e calcular um "produto ímpar" — o produto de todos os fatores primos com expoente ímpar na decomposição. Números com o mesmo produto ímpar são adajcentes. Ao agrupar esses produtos, observamos que grupos com tamanho par (ou produto ímpar igual a 1) se fundem após uma transformação, enquento grupos com tamanho ímpar permanecem isolados. Portanto, para x=0, a resposta é o maior tamanho de grupo; para x>0, a resposta é o máximo entre o maior tamanho de grupo e a soma dos tamanhos dos grupos que se fundem.

#include<bits/stdc++.h>
using namespace std;
#define f(i,a,b) for(register int i=a;i<=b;++i)
#define ll long long
#define N 300005
#define INF 999999999
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline ll read(){ll x;scanf("%lld",&x);return x;}
unordered_map<ll,ll> agrupamento;
ll maxInicial, maxFinal, somaTotal;

void calcularProdutoImpar(ll valor) {
    ll produto=1, temp=valor;
    for(int i=2;i*i<=valor;++i) {
        if(valor%i==0) {
            int cont=0;
            while(valor%i==0) cont++, valor/=i;
            if(cont%2) produto*=i; 
        }
    }
    if(valor!=1) produto*=valor;
    agrupamento[produto]++;
}

void resolver() {
    somaTotal=maxInicial=maxFinal=0;
    agrupamento.clear();
    ll n=read();
    f(i,1,n) calcularProdutoImpar(read());
    for(auto& par : agrupamento) {
        maxInicial=max(maxInicial, par.second);
        if(par.second%2==0 || par.fi==1)
            somaTotal+=par.second;
    }
    maxFinal=max(maxInicial, somaTotal);
    ll q=read();
    f(i,1,q) {
        ll x=read();
        if(!x) printf("%lld\n", maxInicial);
        else printf("%lld\n", maxFinal);
    }
}

int main() {
    ll T=read();
    while(T--) resolver();
    return 0;
}

Problema F: Habitação Estranha

Dado um grafo não direcionado e sem peso, precisamos selecionar alguns vértices para colorir, de modo que: 1) todo vértice seja colorido ou adjacente a um vértice colorido; 2) o grafo permaneça conectado; 3) vértices coloridos não sejam adjacentes. Encontrar tal coloração ou reportar "NO".

A abordagem utiliza busca em profundidade (DFS). Partindo de um vértice inicial, para cada vértice visitado, se nenhum de seus vizinhos foi colorido, colorimos o vértice atual; caso contrário, não o colorimos. Isso garante as condições 1 e 3. A conectividade é mantida pela natureza da DFS em grafos conexos.

#include<bits/stdc++.h>
using namespace std;
#define f(i,a,b) for(register int i=a;i<=b;++i)
#define ll long long
#define N 300005
#define INF 999999999
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline ll read(){ll x;scanf("%lld",&x);return x;}
vector<ll> grafo[N], resultado;
int estado[N];

void buscaProfundidade(ll no) {
    estado[no]=2;
    for(ll vizinho : grafo[no]) {
        if(estado[vizinho]==2) estado[no]=1;
    }
    for(ll vizinho : grafo[no]) {
        if(!estado[vizinho]) buscaProfundidade(vizinho);
    }
}

void resolver() {
    ll n=read(), m=read();
    resultado.clear();
    f(i,1,n) estado[i]=0, grafo[i].clear();
    f(i,1,m) {
        ll u=read(), v=read();
        grafo[u].pb(v);
        grafo[v].pb(u);
    }
    buscaProfundidade(1);
    int contador=0;
    f(i,1,n) {
        if(!estado[i]) {
            printf("NO\n");
            return;
        }
        if(estado[i]==2) resultado.pb(i);
    }
    printf("YES\n");
    int tamanho=resultado.size();
    printf("%d\n", tamanho);
    for(ll x : resultado) printf("%lld ", x);
    printf("\n");
}

int main() {
    ll T=read();
    while(T--) resolver();
    return 0;
}

Notas Técnicas

O conceito de "produto ímpar" é útil em problemas que envolvem paridade de expoentes na fatoração prima, permitindo agrupar números por adjacência defindia por razões de lcm e gcd.

Para decomposição rápida de fatores primos em intervalos até 10^6, podemos pré-calcular o menor fator primo para cada número usando crivo, resultando em complexidade O(n log n) para pré-processamento e O(log n) por consulta.

int menorFator[N];
void preprocessarCrivo(int limite) {
    for(int i=2; i<=limite; ++i) {
        if(menorFator[i]) continue;
        for(int j=i; j<=limite; j+=i)
            menorFator[j]=i;
    }
}

Tags: Codeforces fatoração_prima Grafos busca_em_profundidade C++

Publicado em 6-25 16:33