#include <stdio.h>

long tab[7001][4], n,k,m, pom1,pom2;
char do_kasacji[7001], stop;
long wskaz_kolor[7001], wybrane[7001], SUMA_MAS;
long long ceny_zb[7001], SUMA_CEN;
long na_teraz[100001],na_pozniej[100001];


int SORT_tab(long pocz,long kon) //Przepisane z Cormena
{
long sx,sxx,sxxx,sq,si,sj,pom;
if(pocz<kon)
    {
     sx = tab[kon][1];
     sxx = tab[kon][2];
     sxxx = tab[kon][3];
     si=pocz-1;
     for(sj=pocz;sj<=kon-1;sj++) 
        {
         if (  (tab[sj][1]<sx)  ||  ( (tab[sj][1]==sx)&&(tab[sj][2]<sxx) )  ||  ( (tab[sj][1]==sx)&&(tab[sj][2]==sxx)&&(tab[sj][3]<sxxx) )  )
            {
             si++;
             pom=tab[si][1]; tab[si][1]=tab[sj][1]; tab[sj][1]=pom;
             pom=tab[si][2]; tab[si][2]=tab[sj][2]; tab[sj][2]=pom;
             pom=tab[si][3]; tab[si][3]=tab[sj][3]; tab[sj][3]=pom;
            }
        }
     pom=tab[si+1][1]; tab[si+1][1]=tab[kon][1]; tab[kon][1]=pom;
     pom=tab[si+1][2]; tab[si+1][2]=tab[kon][2]; tab[kon][2]=pom;
     pom=tab[si+1][3]; tab[si+1][3]=tab[kon][3]; tab[kon][3]=pom;
     
     sq=si+1;
     SORT_tab(pocz,sq-1);
     SORT_tab(sq+1,kon);
    }
return 0;
}



int ZWIEKSZ (int poz)
{
// Poza samym zaktualizowaniem wektora 'wybrane', aktualizuję też obie sumy.
// Najpierw odejmuję z pozycji, którą chcę przesunąć.
SUMA_CEN -= tab[wybrane[poz]][3];  SUMA_MAS -= tab[wybrane[poz]][2];

//Teraz przesuwam na tej pozycji, ale są dwie możliwości - albo jest ok, albo muszę "przekręcić" licznik.
if ( wybrane[poz]+1 < wskaz_kolor[poz+1] )
	{ // Jest ok, w porządku, bez żadnych dziwnych ruchów
	 wybrane[poz]++;
	 SUMA_CEN += tab[wybrane[poz]][3];  SUMA_MAS += tab[wybrane[poz]][2];
	}
else  //Kolejny przekroczyłby swój zakres, więc trzeba go wsadzić na początek zakresu...
	{ //  ... a następnie zwiększyć pozycję wcześniej.
	 wybrane[poz] = wskaz_kolor[poz];
	 SUMA_CEN += tab[wybrane[poz]][3];  SUMA_MAS += tab[wybrane[poz]][2];
	 if (poz-1 == 0) stop=1;
	 else ZWIEKSZ(poz-1);
	}
return 0;
}



int main()
{
scanf("%ld",&n);
scanf("%ld",&k);
scanf("%ld",&m);
for (int ii=1; ii<=n; ii++)   scanf("%ld %ld %ld",&tab[ii][1],&tab[ii][2],&tab[ii][3]);


/*// PRZYKŁAD 1:
n=8;  k=3;  m=9;
tab[1][1]=2;  tab[1][2]=4;  tab[1][3]=4;        tab[2][1]=1;  tab[2][2]=1;  tab[2][3]=3;
tab[3][1]=3;  tab[3][2]=7;  tab[3][3]=3;        tab[4][1]=2;  tab[4][2]=4;  tab[4][3]=3;
tab[5][1]=1;  tab[5][2]=1;  tab[5][3]=2;        tab[6][1]=3;  tab[6][2]=1;  tab[6][3]=4;
tab[7][1]=1;  tab[7][2]=4;  tab[7][3]=3;        tab[8][1]=3;  tab[8][2]=7;  tab[8][3]=5;
//tab[][1]=;  tab[][2]=;  tab[][3]=;        tab[][1]=;  tab[][2]=;  tab[][3]=;
//*/// KONIEC PRZYKŁADU 1.

SORT_tab(1,n);
// Sortuje po wszystkich trzech, więc można przejrzeć, czy z jakichś można od razu zrezygnować i je po prostu usunąć?
// Jeśli dwie pierwsze liczby są takie same, to biorę tę wartość, która jest najmniejsza na trzeciej pozycji.

for (int ii=1; ii<=n; ii++)   do_kasacji[ii]=0;
for (int ii=2; ii<=n; ii++)   if ((tab[ii-1][2]==tab[ii][2]) && (tab[ii-1][1]==tab[ii][1]))   do_kasacji[ii]=1;


//for (int ii=1; ii<=n; ii++)   printf("%ld %ld %ld\n",tab[ii][1],tab[ii][2],tab[ii][3]);
//printf("Do kasacji: ");
//for (int ii=1; ii<=n; ii++)  printf("%d ",do_kasacji[ii]);
//printf("\n\n");

//Teraz kasuję. W tej samej tablicy, tylko z dwoma licznikami
pom1=0;pom2=1;
while (pom2<=n)
	{
	 while ((do_kasacji[pom2]==1)&&(pom2<=n)) pom2++;
	 //No i już tylko kopiowanie
	 if (pom2<=n)
	 	{
	 	 pom1++;
	 	 if (pom1<pom2)
	 	 	{
	 	 	 tab[pom1][1] = tab[pom2][1];
	 	 	 tab[pom1][2] = tab[pom2][2];
	 	 	 tab[pom1][3] = tab[pom2][3];
	 	 	}
	 	 pom2++;
	 	}
	}
n=pom1;

//for (int ii=1; ii<=n; ii++)   printf("%ld %ld %ld\n",tab[ii][1],tab[ii][2],tab[ii][3]);

// Teraz muszę zrobić pomocniczą tabelę, która będzie zawierać początki i końce danych kolorów.
for (int ii=0; ii<=k+1; ii++)   wskaz_kolor[ii]=0;
// Po kolei idę po tab... Jeśli jest zmiana koloru, to wpisuję dany początek.
wskaz_kolor[tab[1][1]]=1; // Pierwszy element
for (int ii=2; ii<=n; ii++)   if (tab[ii-1][1] != tab[ii][1])   wskaz_kolor[tab[ii][1]]=ii;
wskaz_kolor[k+1]=n+1; // Zamykający element

//printf("Wskaz kolor: ");
//for (int ii=1; ii<=k+1; ii++)  printf("%ld ",wskaz_kolor[ii]);
//printf("\n");

// Sprawdzam, czy gdzieś jest zero - jeśli tak, to można podać odpowiedź
for (int ii=1; ii<=k; ii++)   if (wskaz_kolor[ii]==0)
	{ // Brakuje co najmniej jednego z kolorów, więc nie kupi żadnych żelków.
	 printf("0\n");
	 for (int jj=1; jj<m; jj++) printf("-1\n");
	 return 0;
	}

// Teraz sprawdzam wszystkie możliwe zbiory "po jednym z każdego koloru".
// Muszę liczyć ich (masę modulo m) oraz (sumę cen - long long)
SUMA_CEN=0;
ceny_zb[0]=0;
for (int ii=1; ii<m; ii++) ceny_zb[ii]=100000000000000000;
// Na początku "wybrane" są takie same, jak wskaźniki kolorów:
for (int ii=1; ii<=k; ii++)   { wybrane[ii] = wskaz_kolor[ii];  SUMA_CEN += tab[wybrane[ii]][3];  SUMA_MAS += tab[wybrane[ii]][2]; }
// No i pierwszego można już wpisać do tabeli...
// (SUMA_MAS % m) - reszta z dzielenia
if (ceny_zb[SUMA_MAS % m] > SUMA_CEN)   ceny_zb[SUMA_MAS % m] = SUMA_CEN;


// No i teraz w pętli muszę rozpatrzyć wszystkie możliwe zestawy...
stop=0;
while (!stop)
	{
	 ZWIEKSZ(k);
	 // Tutaj mam już nową konfigurację z już gotowymi SUMĄ_CEN oraz SUMĄ_MAS
	 if (stop==0) 
	 	{
	 	 if (ceny_zb[SUMA_MAS % m] > SUMA_CEN)   ceny_zb[SUMA_MAS % m] = SUMA_CEN;
	 	}
	}

// No i tutaj powinienem już mieć całą listę możliwych zbiorów (a raczej tylko najlepszych z nich).

//printf("\nCeny dla kolejnych modulo:\n");
//for (long ii=0; ii<m; ii++)   printf("ceny[%ld] = %lld\n",ii,ceny_zb[ii]);

// Przygotowuję tablicę z indeksami, do których mam spróbować dodać kolejne ruchy
for (int ii=0; ii<=100000; ii++)  { na_teraz[ii]=0; na_pozniej[ii]=0; }
for (int ii=1; ii<m; ii++)   if(ceny_zb[ii] < 100000000000000000)  
	{
	 na_pozniej[0]++;
	 na_pozniej[na_pozniej[0]]=ii;
	}

while(na_pozniej[0]>0) //Dopóki jest cokolwiek do sprawdzania jeszcze
	{
	 // Najpierw przepisuję z na_później do na_teraz.
	 for (int ii=0; ii<=na_pozniej[0]; ii++)   na_teraz[ii]=na_pozniej[ii];
	 // Wyzerowanie tabeli 'na_pozniej':
	 na_pozniej[0]=0;
	 
	 for (int bazowy=1; bazowy<=na_teraz[0]; bazowy++)
	 	{ // Dla każdego bazowego elementu sprawdzamy, czy możemy dodać którykolwiek, aby polepszyć
	 	 for (int jakis=1; jakis<m; jakis++)   if (ceny_zb[jakis] < 100000000000000000)
	 	 	{ // Sprawdzamy, czy wspólna masa modulo oraz wspólna cena jest lepsza
	 	 	 SUMA_CEN = ceny_zb[na_teraz[bazowy]] + ceny_zb[jakis];
	 	 	 SUMA_MAS = na_teraz[bazowy] + jakis;
	 	 	 if (ceny_zb[SUMA_MAS % m] > SUMA_CEN)   ceny_zb[SUMA_MAS % m] = SUMA_CEN;
	 	 	}
	 	}
	}

// I zostało wypisanie wyniku :)
//printf("WYNIK:\n\n");


for (int ii=0; ii<m; ii++)   
	{
	 if (ceny_zb[ii] < 100000000000000000)   printf("%lld\n",ceny_zb[ii]);
	 else printf("-1\n");
	}


return 0;
}




