Cod sursa(job #2986559)

Utilizator mmocanuMocanu Mihai-Adrian mmocanu Data 28 februarie 2023 19:28:09
Problema Heavy Path Decomposition Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 7.48 kb
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#define MAXN 100005 ///Valoarea maxima a lui N
#define INFI 100005 ///Valoarea maxima a adancimi unui nod
#define bol bool ///Fixatie ca toate sa fie aliniate(bool are 4 litere, int are 3)
using namespace std;

///Arborele il construim din nodul 1

struct NOD{
  int cost; ///Costul Nodului
  int last; ///Ultima pozitie in sirul eulerian al nodului
  int adan; ///Adancimea nodului
  int pozi; ///Indica care este secventa din vectorul stdr care se afla nodul
  int loca; ///Locatia la care de afla nodul in vectorul siru
  int fath; ///Tatal nodului
  bol hevy; ///Este true cand este un nod heavy
  bol visi; ///Pentru Pargurgerea cu DFS
};

struct LFD{
  int left;    ///Limita Stanga
  ///int righ; ///Limita Dreapta --- neutilizat
};


vector <int> graf[MAXN];///Graful in sine
NOD poit[MAXN];   ///Nodurile Grafului
LFD stdr[MAXN];   ///Pozitiile intervalelor de Heavy Pathuri
int aint[MAXN*4]; ///Aint-ul pentru gasirea maximului din vectorul siru
int siru[MAXN];   ///Sirul format de secvente de Heavy Pathuri
int eule[MAXN*2]; ///Pargurgerea Euleriana a Arborelui
int alca[MAXN*8]; ///Aint-ul pentru determinarea LCA
int t;

int Min(int x,int y){
  return x<y?x:y;
}

int Max(int x,int y){
  return x>y?x:y;
}

int DFS(int node,int adan){
  int ma,x,s,aux;

  s=1;
  ma=-1;///Folosim Maximul pentru determinarea care este copilul Heavy
  x=-1; ///Indicele copilului Heavy

  poit[node].adan=adan;
  poit[node].visi=true;

  poit[node].last=t;///Formam parcurgerea euleriana
  eule[t++]=adan;   ///Setam ultima pozitie la care se afla nodul in parcurgerea euleriana in acest moment

  for(int neigh : graf[node]){ ///Pargurgem Fii
    if(!poit[neigh].visi){

      aux=DFS(neigh,adan+1);

      poit[node].last=t; ///Formam parcurgerea euleriana
      eule[t++]=adan;    ///Setam ultima pozitie la care se afla nodul in parcurgerea euleriana in acest moment

      if(ma<aux){        ///Determinam Heavy Child
        ma=aux;
        x=neigh;
      }
      s+=aux;
    }
  }

  if(x!=-1){
    poit[x].hevy=true; ///Setam un fiu ca Heavy Child
  }

  return s; ///Nr de noduri din acest subarbore
}

void CreateLCA(int x,int y,int poz){ ///Formam un Aint pentru sirul eulerian
  int mij;
  if(x==y){
    alca[poz]=eule[x];
    return;
  }
  mij=(x+y)/2;
  CreateLCA(x    ,mij,poz*2  );
  CreateLCA(mij+1,y  ,poz*2+1);
  alca[poz]=Min(alca[poz*2],alca[poz*2+1]);
}

int LCA(int x,int y,int i,int j,int poz){ ///Determinam valoare minima din sirul eulerian de la i la j
  int mij;
  if(i<=x && y<=j){
    return alca[poz];
  }
  if(y<i || j<x){
    return INFI;
  }
  mij=(x+y)/2;
  return Min(LCA(x    ,mij,i,j,poz*2  ),
             LCA(mij+1,y  ,i,j,poz*2+1));
}

void MakeHeavyDecomp(int n){  ///Folosindune de copiii light formam un lant de la fiecare copil light la o frunza doar prin copii Heavy
  int i,m,t,s,node;

  for(i=1;i<=n;i++){ ///Setam ca nu am vizitat niciun nod
    poit[i].visi=0;
  }
  m=0;
  t=1;
  for(i=1;i<=n;i++){
    if(poit[i].hevy==0){
      stdr[m].left=t; ///Setam pozitia de inceput a lantului de un copil Light si de restul Heavy
      s=1;
      node=i;
      poit[node].visi=1;///Am visitat nodul
      while(s==1){ ///Parcurgem pana nu mai sunt fii Heavy
        poit[node].loca=t; ///Setam loca nodului cu pozitia nodului in sir
        siru[t++]=node; ///Adaugam la sir acest nod
        poit[node].pozi=m; ///Setam pozi nodului cu pozitia lantului de un copil Light si de restul Heavy
        s=0;///Nu stim daca avem copil Heavy
        for(int neigh : graf[node]){
          if(poit[neigh].hevy && !poit[neigh].visi){
            node=neigh;
            poit[neigh].visi=1; ///Visitam nodul
            s=1; ///Avem un fiu Heavy
          }
        }
      }
      m++; ///Trecem la urmatorul lant
    }
  }
}

void CreateAINT(int x,int y,int poz){///Formam un aint cu costurile nodurilor care se afla in vectorul siru
  int mij;
  if(x==y){
    aint[poz]=poit[siru[x]].cost;
    return;
  }
  mij=(x+y)/2;
  CreateAINT(x    ,mij,poz*2  );
  CreateAINT(mij+1,y  ,poz*2+1);
  aint[poz]=Max(aint[poz*2],aint[poz*2+1]);
}

int SearchAINT(int x,int y,int i,int j,int poz){///Gasim maximul din aint de la i la j
  int mij;
  if(i<=x && y<=j){
    return aint[poz];
  }
  if(j<x || y<i){
    return 0;
  }
  mij=(x+y)/2;
  return Max(SearchAINT(x    ,mij,i,j,poz*2  ),
             SearchAINT(mij+1,y  ,i,j,poz*2+1));
}

void UpdateAINT(int x,int y,int i,int val,int poz){///Schimbam valoarea unei pozitiei i din aint cu val
  int mij;
  if(x==i && i==y){
    aint[poz]=val;
    return;
  }
  if(i<x || y<i){
    return;
  }
  mij=(x+y)/2;
  UpdateAINT(x    ,mij,i,val,poz*2  );
  UpdateAINT(mij+1,y  ,i,val,poz*2+1);
  aint[poz]=Max(aint[poz*2],aint[poz*2+1]);
}

int main(){
  int n,m,i,x,y,inter,st,dr,r,j,aux,last;
  FILE *fin,*fout;
  fin=fopen("heavypath.in","r");
  fout=fopen("heavypath.out","w");
  fscanf(fin,"%d%d",&n,&m);

  for(i=1;i<=n;i++){
    fscanf(fin,"%d",&poit[i].cost);
    poit[i].fath=-1; ///Initializam parinti nodurilor cu -1
  }

  for(i=1;i<n;i++){
    fscanf(fin,"%d%d",&x,&y);
    if(poit[y].fath==-1){ ///Setam parintele nodului
      poit[y].fath=x;
    }else{
      poit[x].fath=y;
    }
    graf[x].push_back(y);
    graf[y].push_back(x);
  }

  DFS(1,1); ///Pornim un DFS din nodul 1 cu adancimea 1
  CreateLCA(1,2*n-2,1);
  MakeHeavyDecomp(n);
  CreateAINT(1,n,1);

  for(i=0;i<m;i++){
    fscanf(fin,"%d%d%d",&t,&x,&y);
    if(t==0){
      UpdateAINT(1,n,poit[x].loca,y,1); ///Schimbam valoarea
    }else{
      st=poit[x].last; ///Luam ultimile pozitii ale nodurilor x si y din parcurgerea euleriana
      dr=poit[y].last;
      if(st>dr){ ///Facem ca st sa fie intotdeauna mai mic ca dr
        aux=dr;
        dr=st;
        st=aux;
      }
      inter=LCA(1,2*n-2,st,dr,1); ///Determinam inaltimea punctului de intersectie
      r=-1; ///Initializam raspunsul cu -1(Nu ne afecteaza Maximul)
      for(j=0;j<2;j++){ ///Generalizam cele doua parcurgeri din lant in lant pana la intersectie
        if(j==0){
          last=x; /// Mai intai facem cu x
        }else{
          last=y; /// Dupa facem cu y
        }
        ///last este primul nod pe care vrem sa il parcurgem dar nu l-am parcurs

        while(poit[siru[stdr[poit[last].pozi].left]].adan>inter){ ///Luam pozitia de start a intervalului in care se afla nodul last si verificam ca inaltimea este mai mare ca inaltimea intersectiei
          r=Max(r,SearchAINT(1,n,stdr[poit[last].pozi].left,poit[last].loca,1)); ///Facem maximul dintre raspunsul pe care il avem si maximul din intervalul pozitiei de inceput al lantului in care se afla si nodul curent
          last=poit[siru[stdr[poit[last].pozi].left]].fath;///Setam nodul nou care este primul, ca fiind tatal pozitiei de start al lantului in care se afla nodul last
        }
        r=Max(r,SearchAINT(1,n,stdr[poit[last].pozi].left+inter-poit[siru[stdr[poit[last].pozi].left]].adan,poit[last].loca,1)); /// Am ajuns sa nu mai parcurgem tot lantul astfel facem maximul din intervalul pozitie la care se afla intersectia si nodul last
        ///Determinam nodul la care se afla pozitia de intersectie folosind o formula luand pozitia de start a lantului in sir si adunand diferenta de inaltimi dintre ele, care ne aduce la pozitia de intersectie
      }
      fprintf(fout,"%d\n",r);
    }
  }

  fclose(fin);
  fclose(fout);
  return 0;
}