This submission is migrated from previous version of oj.uz, which used different machine for grading. This submission may have different result if resubmitted.
// paths_100_panaete.cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 200001;
int n,k,e,T[N],F[N],g[N],lf,u=1,a,b,getLeaf(int nod);
int64_t sol[N],cT[N],cD[N],costAll,*DU,*DA,*DB,U,A,B,costK,bestOfTheRest;
vector<pair<int,int64_t>> v[N];
queue<int> q;
vector<pair<int64_t,int>> P;
void readTree(),cazGeneral(),cazParticular(),dfs(int,int64_t*,int&,int64_t&);
int main()
{
readTree();
if(k==1)cazParticular();
cazGeneral();
return 0;
}
void readTree()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin>>n>>k;
for(int i=1; i<n; i++)
{
int x,y;
int64_t c;
cin>>x>>y>>c;
costAll+=c;
v[x].push_back(make_pair(y,c));
g[x]++;
v[y].push_back(make_pair(x,c));
g[y]++;
}
}
void cazGeneral()
{
/// imi creez o coada cu toate frunzele
for(int i=1; i<=n; i++)if(g[i]==1)
{
q.push(i);
lf++;
}
/// BEGIN : caz particular nr_frunze<k -> pentru orice nod X solutia este costul total al arborelui
if(lf<=k)
{
for(int i=1; i<=n; i++) cout<<costAll;
exit(0);
}
/// END
/// incep o parcurgere simultana din toate frunzele construind o descompunere a arborelui in drumuri
e=n-1;
int root;
while(e) /// parcurgerea se va finaliza cand am utilizat toate muchiile
/// cand parcurg o muchie scad gradele la capetele acesteia
{
int nod=q.front(); /// in coada voi avea la un moment dat orice nod care mai are un singur vecin accesibil
/// [initial aceste noduri sunt frunzele
/// dar pe masura ce fac parcurgerea adaug noduri care mai au exact un vecin neparcurs]
q.pop();
g[nod]--; /// iau un nod care are grad "rezidual" 1 ii reduc gradu la 0 si astfel el nu va mai fi accesat
for(auto it:v[nod])
{
int dad;/// acesta va fi nodul spre care prelungesc drumul curent
/// pentru nodul curent acest nod va capata statut de "tata"
int64_t cst;
tie(dad,cst)=it;
if(g[dad]) /// tatal va fi singurul nod care mai are grad rezidual
{
e--;/// parcurg muchia
T[nod]=dad; /// am stabilit tatal in aceasta parcurgere
root=dad; /// la final, radacina arborelui va fi capatul ultimei muchii parcurse
cT[nod]=cst; /// memorez costul muchiei de la oricare nod spre tatal sau.
/// Acest cost va face parte din costul total al drumului pe care se gaseste nodul curent
/// pentru fiecare nod voi stabili un fiu favorit
/// Fiecare nod face parte dintr-un drum
/// Fiul favorit este sub nod pe drumul spre frunza
if(!F[dad]) /// cazul in care in nodul tata nu a mai ajuns inca niciun drum
///nodul curent este deocamdata singurul candidat la fiu favorit pentru tata
{
F[dad]=nod;
cD[dad]=cD[nod]+cT[nod]; /// cD[dad] costul in jos(spre frunza) al nodului la care m-am legat
}
else if(cD[nod]+cT[nod]>cD[dad]) /// cazul cand nod are deja un frate si dad il are deocamdata ca fiu favorit
/// dar eu as crea un drum mai bun spre frunza mea decat fratele !!!
{
int bro=F[dad];/// acesta este fratele pe care "il detronez" si devin eu ca fiu favorit
P.push_back(make_pair(cD[dad],bro));/// celalat drum este "eliminat" din arbore
cD[dad]=cD[nod]+cT[nod]; /// imi preiau rolul de fiu favorit
F[dad]=nod;
}
else P.push_back(make_pair(cD[nod]+cT[nod],nod));/// cazul cand fratele favorit isi pastreaza statutul
/// drumul nodului curent este eliminat din arbore
g[dad]--;/// anunt ca inca un fiu a adus un drum in dad
if(g[dad]==1)q.push(dad);/// daca gradul rezidual in dad a ramas 1, atunci din dad pot prelungi drumul cel mai mare
/// acest drum ajunge in tata prin "fiul favorit"
}
}
}
/// dupa ce am parcurs ultima muchie am prelungit cel mai lung drum corespunzator unei frunze
/// acest drum nu a fost inca adaugat la descompunerea arborelui in drumuri
/// il adaug acum
/// Observatie importanta = acum avem capatul ultimului drum drept radacina a arborelui in care am stabilit deja>
/// vectorul de tati T[] (fiecare nod cu exceptia radacinii are tatal)
/// vectorul de fii favoriti F[] (fiecare nod cu exceptia frunzelor are un fiu favorit)
/// vectorul F[] recupereaza drumul din oricare nod spre frunza "proprie"
P.push_back(make_pair(cD[root],root));
/// imi sortez drumurile in ordine descrescatoare dupa lungime
sort(P.begin(),P.end());
reverse(P.begin(),P.end());
/// Ce este special legat de aceasta descompunere???
/// sa presupunem ca am strict mai mult de k+1 frunze
/// se poate arata ca frunza A a ultimului(celui mai scurt drum) nu trebuie aleasa pentru solutia NICIUNUI NOD N !!!
/// demonstratie prin reducere la absurd.
/// Evident ca daca as alege A, obligatoriu as avea in solutie tot pathul corespunzator acelei frunze.
/// Fie X nodul in care se agata drumul frunzei A la restul arborelui.
/// acum am doua cazuri
/// cazul 1: X are grad 2 in arborele solutie
/// atunci este suficient sa aleg in locul lui A oricare alta frunza B nealeasa care ajunge in X prin vectorul de tati
/// tot drumul B-X se adauga la solutie si evident ca acesta este mai lung decat A-X
/// daca nodul N este pe drumul A-X se pierde din solutie doar portiunea A-N a drumului A-X
/// altfel se pierde intregul drum A-X
/// dar tot se obtine o solutie mai buna
/// cazul 2: X are grad strict mai mare ca 2 in arborele solutie
/// atunci este suficient sa aleg in locul lui A oricare alta frunza B nealeasa
/// Din B se ajunge prin vectorul de tati un nod Y al arborelui solutie
/// Tot drumul B-Y se adauga la solutia modificata si acest drum sigur este mai lung decat A-X
/// Prin eliminarea lui A se pierde drumul de la A pana la cel mai apropiat nod ales dintre N,Y si X deci se va pierde cel mult
/// lungimea drumului A-X
///Acum putem elimina acest path din arbore si solutia finala pentru nodurile neeliminate va ramane aceeasi
///Sa presupunem ca am rezolvat problema in arborele ramas.
///Ar mai ramane sa rezolvam problema pentru path-ul eliminat
///MAGIC-> se poate demonstra ca solutia pentru toate nodurile N din path este egala cu solutia din X adunata cu lungimea drumului N-X
///Acum cu aceste idei
/// - pot elimina pe rand toate drumurile pana raman K+1 drumuri(sau echivalent K+1 frunze)
/// rezolv problema pentru arborele cu cele mai bune K+1 drumuri(frunze)
/// adaug si rezolv in oridnea inversa eliminarii celelalte drumurile eliminate
/// Mi-a mai ramas sa rezolv problema pentru un arbore cu exact K+1 frunze si stiu descompunerea acestui arbore in drumuri
/// prima impresie ar fi ca optim ar fi sa aleg cele mai bune K frunze
/// .... dar nu e chiar asa
/// uneori e mai bine sa folosesc inlocuiesc o funza cu a k+1 frunza (numita in continuare "best of the rest"
/// memorez cel mai slab drum din cele k+1
bestOfTheRest=P[k].first;
/// imi calculez costul arborelui AK cu cele mai bune k frunze
for(int i=0; i<k; i++)costK+=P[i].first;
/// rezolv arborele AK
for(int i=0; i<k; i++) for(int nod=P[i].second; nod; nod=F[nod])
{
/// in mod normal as folosi exact cele K frunze
sol[nod]=costK;
/// singurul motiv pentru care nu as face asta?
/// sunt prea aproape de o frunza( frunza de pe drumul meu)
/// daca distanta pana la frunza mea este mai mica decat "best of the rest"
/// prefer sa inlocuiesc acea frunza
/// pierd distanta pana la acea frunza
/// dar castig prin inlocuirea sa cu al k+1 drum
if(bestOfTheRest>cD[nod])sol[nod]+=bestOfTheRest-cD[nod];
}
/// aici aplic ceea ce spuneam mai sus -> rezolv path-urile in ordinea inversa eliminarii
/// acelasi principiu de rezolvare e valabil si pentru al K+1 lea path care va fi tratat
/// ca ultimul eliminat ( deci primul care va fi readaugat si rezolvat)
for(int i=k; i<lf; i++) for(int nod=P[i].second; nod; nod=F[nod])sol[nod]=sol[T[nod]]+cT[nod];
for(int i=1; i<=n; i++) cout<<sol[i]<<'\n';
}
void cazParticular()
{
///Cazul k=1
/// se determina un diametru A-B
/// pentru orice nod X solutia = max( dist(X,A) , dist(X,B) )
DU=sol;
DU[u]=1;
dfs(u,DU,a,A);
DA=cT;
DA[a]=1;
dfs(a,DA,b,B);
DB=cD;
DB[b]=1;
dfs(b,DB,u,U);
for(int i=1; i<=n; i++)cout<<max(DA[i],DB[i])-1<<'\n';
exit(0);
}
void dfs(int nod,int64_t *dst,int &bst,int64_t &Bst)
{
if(dst[nod]>Bst)
{
Bst=dst[nod];
bst=nod;
}
for(auto it:v[nod])if(!dst[it.first])
{
dst[it.first]=dst[nod]+it.second;
dfs(it.first,dst,bst,Bst);
}
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |