Cod sursa(job #343337)

Utilizator mgntMarius B mgnt Data 25 august 2009 15:11:00
Problema Atac Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 6.64 kb
// Solutia cauta repetat LCA folosind RMQ asa cum e scris in articol:
// http://infoarena.ro/lowest-common-ancestor
//
// Apoi cum zice un post de pe blog, costul drumului intre noduri este
// gasit cumva ca in problema stramosi.

#include<cstdio>
#include<vector>
using namespace std;

int const maxn=32000; // numarul maxim de noduri
int const maxp=15; // 2^maxp > maxn
int const maxlun=2*(maxn-1)+1;
int N, M, P;

struct Ed { int to, ct; }; Ed E[maxn-1], Adj[2*(maxn-1)];

// Daca se face un subarbore copil al unei frunze, nodul
// din care s-a scos nu se mai repeta o data in Str, iar
// nodul in care s-a pus se repeta o data in plus. Astfel
// pentru orice arbore se foloseste un numar fix de pozitii
// in Str si anume 2*(N-1)+1

// Inaltimea intr-un arbore de segmente pentru un interval cu N
// elemente este H=[logN]+1. Pentru ca intervalele se injumatatesc
// de la parinte la copil. Astfel, numarul de noduri este maxim
// 2^0+2^1+...+2^H=2^{H+1}-1. Iese in mare mai putin de 5*N. 

int
  Str[maxlun] // o listare convenabila pentru a calcula lowest common ancestor
, Val[1+maxn] // adancimea folosita ca valoare in range minimum query ce da LCA-ul
, Pos[1+maxn] // o pozitie ca intrare pentru RMQ
, Lun // lungimea lui Str
, NC[1+maxn] // neighbour count
, NP[2+maxn] // neighbour pos
, NI[1+maxn] // neighbour index
, St[maxn][2]  // stack folosit in calculul Str
, Min[1+(4*maxlun)] // folosite in implementarea arborelui de intervale folosit in RMQ
, A[maxn][maxp] // folosit ca in stramosi
, C[maxn][maxp] // extensie la stramosi
;

// Segment tree (arbore de intervale) : operatii update, query
// [ opereaza pe Min cu Val ]

// Se trece dintr-un interval intr-altul de lungime injumatatita pana
// cand se ajunge la un interval punct, asa ca T(n)=T(n/2)+O(1) ce da
// T(n)=O(log(N)) pentru un arbore pe intervalul 1..N
void update (int q, int st, int dr, int poz, int val) {
  if ( (poz <= st) && (dr <= poz) ) {
    if (  (-1 == Min[q]) || (Val[Min[q]]>Val[val])  ) { Min[q]=val; }
    return;
  }
  int mij=(( st + dr ) >> 1), q1=(q << 1), q2=(1 + q1);
  if (poz <= mij) { update (q1, st, mij, poz, val) ;
    if (  (-1==Min[q]) || (Val[Min[q]]>Val[Min[q1]])  ) { Min[q]=Min[q1]; }
  } else { update (q2,1+mij, dr, poz, val) ;
    if (  (-1==Min[q]) || (Val[Min[q]]>Val[Min[q2]])  ) { Min[q]=Min[q2]; }
  }
}

// Cand se intra intr-un interval stanga, tot timpul cazul interval dreapta
// se rezolva in O(1), similar pentru cand se intra intr-un interval dreapta.
// T(n)=T(n/2)+O(1) -> O(log(N)) pentru un arbore pe intervalul 0..N
int query (int q, int st, int dr, int a, int b) {
  if ( (a<=st) && (dr<=b) ) {
    return Min[q] ;
  }
  int mij=(( st + dr ) >> 1), q1=(q << 1), q2=(1+q1), x=-1, y=-1;
  if (a <= mij) { x = query (q1, st, mij, a, b); }
  if (b >  mij) { y = query (q2 , mij+1, dr, a, b); }
  if (-1==x) { return y;} if (-1==y) {return x;}
  if (Val[x]>Val[y]) {return y;} return x;
}

// Calculeaza costul minim intre un nod i si
// un stramos r ; se foloseste adancimea Val
// pentru a se stii cate muchii sunt de urcat
// de la i pa na la r. Apoi se urca in mod
// binar folosindu-se C[][] pentru a lua
// minimul cat mai multor numere intre i si r
// si A[][] pentru a se schimba i-ul cand
// nu mai putem creste al doilea indice.
int
minCost (int i, int r) {
  if ( i==r ) { return -1; }
  int k = Val[i]-Val[r]; // numarul de muchii intre i si r
  int s=1, miin=C[i][0], z=0;
  while ( s<=k ) {
    if ( miin>C[i][z] ) { miin=C[i][z]; }
    if ( 2*s<k ) { ++z; s*=2; } else { i=A[i][z]; k-=s; z=0; s=1; }
  }
  return miin;
}

int main ( ) {
  FILE *fi= fopen("atac.in","r"); fscanf(fi,"%d %d %d", &N, &M,&P);
  int s, mi, re, X, Y, cA, cB, cC, cD, sp, el, in, lca, hd, tl, ct, po, a, b, el2;
  for ( s=2; N>=s; ++s ) { fscanf(fi,"%d %d", &E[s-2].to, &E[s-2].ct); }
  fscanf(fi, "%d %d %d %d %d %d", &X, &Y, &cA, &cB, &cC, &cD); fclose(fi);
  // Scriu lista de adiacenta intr-un mod mai compact:
  // Calculez numarul de vecini pentru fiecare nod, apoi numar numarul
  // de vecini pana la
  for ( s=1; N>=s; ++s ) { NC[s]=0; }
  for ( s=2; N>=s; ++s ) { ++NC[s]; ++NC[E[s-2].to]; }
  NP[1]=0; for ( s=2; N+1>=s; ++s ) { NP[s]=NP[s-1]+NC[s-1]; }
  for ( s=1; N>=s; ++s ) { NI[s]=NP[s]; }
  for ( s=2; N>=s; ++s) { tl=s; hd=E[s-2].to; ct=E[s-2].ct;
    Adj[NI[tl]].to=hd; Adj[NI[tl]].ct=ct; ++NI[tl];
    Adj[NI[hd]].to=tl; Adj[NI[hd]].ct=ct; ++NI[hd];
  }
  // Calculez Str, Val, Pos, Lun, A[i][0], C[i][0]
  for ( s=1; N>=s; ++s ) { Pos[s]=-1; Val[s]=-1; }
  St[0][0]=1; St[0][1]=NP[1]; A[1][0]=0; C[1][0]=0; sp=0; while (0<=sp) { el=St[sp][0]; in=St[sp][1];
    if (-1 == Pos[el]) { Pos[el]=Lun; Val[el]=sp; } Str[Lun++]=el;
    while ( (NP[1+el]>in) && (-1 != Pos[Adj[in].to]) ) { ++ in; }
    if ( NP[1+el]>in ) { St[sp][1]=in; ++sp; el2=Adj[in].to; St[sp][0]=el2; St[sp][1]=NP[el2]; 
                         A[el2][0]=el; C[el2][0]=Adj[in].ct;
                       }
    else { --sp; }
  }
  // Pregatesc arborele de intervale.
  for ( s=1,re=4*Lun; re>=s; ++s ) { Min[s]=-1; }
  for ( s=0; Lun>s; ++s ) { update(1, 0, Lun-1, s, Str[s]); }
  // Calculez costurile ca la stramosi
  // A[i][j] - stramos cu 2^j muchii pana la i
  // A[i][j+1] = A[A[i][j]][j] pentru ca urcam 2^j muchii pana la A[i][j]
  //                           si de acolo inca 2^j muchii
  // C[i][j] - costul pe muchiile dintre i si A[i][j]
  //           [ costul il luam ca minimul costurilor muchiilor ]
  // C[i][j+1] = min { C[i][j] , C[A[i][j]][j] }
  mi=0; po=1; while ( 2*po<N ) { ++mi; po*=2;
    for (s=1; N>=s; ++s) {
      if (0 == A[s][mi-1]) {
        A[s][mi]=0; C[s][mi]=-1;
      } else {
        if (0==A[A[s][mi-1]][mi-1]) {
          A[s][mi]=0; C[s][mi]=-1;
        } else {
          A[s][mi]=A[A[s][mi-1]][mi-1];
          C[s][mi]=C[A[s][mi-1]][mi-1]>C[s][mi-1]?C[s][mi-1]:C[A[s][mi-1]][mi-1];
        }
      }
    }
  }
  // Misc X, Y folosind relatia data. La fiecare miscare se calculeaza un LCA
  // in log(N) si 2 min cost, fiecare in log(N). Dau O(M*log(N)) operatii.
  fi=fopen ("atac.out", "w");
  for ( s=1; M>=s; ++s ) {
    if (X==Y) {
      if (s>M-P) { fprintf (fi, "0\n"); }
      mi=0;
    } else {
      a=Pos[X]; b=Pos[Y]; if (a>b) { po=a; a=b; b=po; }
      lca=query(1,0,Lun-1,a,b);
      mi=minCost(X, lca); // cel putin unul din X si Y
      re=minCost(Y, lca); // nu-i lca, asa ca mi si re nu-s
                          // in acelasi timp -1
      if (-1==mi) { mi=re; } if (-1==re) { re=mi; }
      if (mi>re) { mi=re; }
      if ( s>M-P ) { fprintf(fi, "%d\n", mi); }
    }
    X=((X*cA +Y*cB)%N)+1;
    Y=((Y*cC+mi*cD)%N)+1;
  }
  fclose(fi);
  return 0;
}