Cod sursa(job #2877361)

Utilizator NanuGrancea Alexandru Nanu Data 24 martie 2022 17:19:50
Problema Radiatie Scor 60
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 3.63 kb
#include <fstream>
#include <algorithm>
#include <vector>

using namespace std;

ifstream fin("radiatie.in");
ofstream fout("radiatie.out");

#define DIM 15000
#define INF (1LL << 30)
typedef pair <int, int> PII;

int n, m, q, k;
bool sel[DIM + 1];  //ce noduri parcurg in dfs;
int sef[DIM + 1];   
int Euler[4 * DIM + 1], nivel[4 * DIM + 1];    //pentru lca;
int tree[8 * DIM + 1], ap[4 * DIM + 1];
PII t[DIM + 1];             //retin tatal unui nod si costul;
vector <PII> Apm[DIM + 1];
struct nanu {
  int x, y, c;
}M[2 * DIM + 1];

static inline bool cmp(nanu a, nanu b) {
  return (a.c < b.c);
}

static inline int radacina(int nod) {
  if(sef[nod] == nod)
    return nod;
  return sef[nod] = radacina(sef[nod]);
} 

static inline void Union(int x, int y) {
  if(x < y)
    sef[y] = x;
  else sef[x] = y;
}

static inline void Kruskal() {
  for(int i = 1; i <= n; i++)
    sef[i] = i;     //fiecare element reprezinta o comp. conexa;

  sort(M + 1, M + m + 1, cmp);    //sortez muchiile crescator dupa cost;
  for(int i = 1; i <= m; i++) {
    int rad1 = radacina(M[i].x);
    int rad2 = radacina(M[i].y);

    if(rad1 != rad2) {
      Union(rad1, rad2);
      Apm[M[i].x].push_back({M[i].y, M[i].c});
      Apm[M[i].y].push_back({M[i].x, M[i].c});
    }
  }
}

static inline void dfs(int nod, int lvl, int tata, int cost) {
  sel[nod] = 1;
  t[nod] = {tata, cost};

  Euler[++k] = nod;
  nivel[k] = lvl;
  ap[nod] = k;        //prima aparitie pe care se afla nodul nod;

  for(auto e : Apm[nod])
    if(!sel[e.first] && t[nod].first != e.first) {
      dfs(e.first, lvl + 1, nod, e.second);

      Euler[++k] = nod;
      nivel[k] = lvl;
    }
}

static inline void Build(int st, int dr, int nod) {
  if(st == dr)
    tree[nod] = st; //retin indicele din parcurgerea Euleriana;
  else {
    int mid = (st + dr) >> 1;
    Build(st, mid, nod * 2);
    Build(mid + 1, dr, nod * 2 + 1);

    if(nivel[tree[nod * 2]] < nivel[tree[nod * 2 + 1]])
      tree[nod] = tree[nod * 2];
    else tree[nod] = tree[nod * 2 + 1];
  }
}

static inline int Query(int st, int dr, int nod, int left, int right) {
  int min1 = 0, min2 = 0;
  if(left <= st && dr <= right) 
    return tree[nod];

  int mid = (st + dr) >> 1;
  if(left <= mid)
    min1 = Query(st, mid, nod * 2, left, right);
  if(mid < right)
    min2 = Query(mid + 1, dr, nod * 2 + 1, left, right);
  
  if(nivel[min1] < nivel[min2])
    return min1;
  return min2;
}

static inline int lca(int x, int y) {
  int a = ap[x], b = ap[y];
  if(a > b)
    swap(a, b);
  
  int nod = Query(1, k, 1, a, b);   //nivelul minim care se afla in intervalul a, b;
  return Euler[nod];                //nodul de pe acel nivel;
}

static inline int Path_to_lca(int nod, int stramos) {
  int maxim = 0;
  while(nod != stramos) {
    maxim = max(maxim, t[nod].second);
    nod = t[nod].first;
  }
  return maxim;
}

int main() {
  fin.tie(0);
  fout.tie(0);

  fin >> n >> m >> q;
  for(int i = 1; i <= m; i++)
    fin >> M[i].x >> M[i].y >> M[i].c;    //retin muchiile;

  //Retin arborele de cost minim pentru a avea cele mai mici canale intre puncte;
  Kruskal();

  //Voi folosi lca pentru complexitate;
  //Un bfs din x in y inseamna O(q * (N + M));

  k = 0;          //numarul de noduri din parcurgerea euleriana;
  nivel[0] = INF; //santinela;
  dfs(1, 0, 0, 0);

  Build(1, k, 1);

  int x, y;
  for(int i = 1; i <= q; i++) {
    fin >> x >> y;

    int stramos = lca(x, y);
    
    int drum1 = Path_to_lca(x, stramos);
    int drum2 = Path_to_lca(y, stramos);
    fout << max(drum1, drum2) << '\n';
  }

  return 0;
}