Idi na tekst

5 - Vrednost dodele

Vremensko ograničenje Memorijsko ograničenje
1600ms 1024MB

Maca Jaca ima za vas sledeći zadatak: Daće vam brojeve N,M a zatim niz B=[B1,B2,,BM], za svaki element važi 1BiM (vrednosti brojeva mogu da se ponavljaju). Vaš zadatak je da formirate niz A=[1,2,,N] (niz se indeksira od 1), a da zatim, za svako i iz niza [0,1,,NM] (u rastućem redosledu) uradite sledeću operaciju:

for (int j=1; j<=M; j++)
  A[i + B[j]] = A[i + j]
Drugim rečima, da za svako j počev od 1 do M (u rastućem redosledu), vrednost u Ai+j upišete u element Ai+Bj. Za kraj, Jaca će vam dati ceo broj C. Vaš zadatak je da izračunate vrednost X=i=1N(Ci×Ai) po modulu 998244353 nakon svih operacija.

Opis ulaza

U prvoj liniji standardnog ulaza nalaze se dva prirodna broja N i M. U narednom redu se nalaze brojevi B1,B2,,BM, odvojeni razmakom. U naredom redu nalazi se jedan ceo broj C.

Opis izlaza

U jedinu liniju standardnog izlaza ispišite traženi broj X (0X<998244353).

Primer 1

Ulaz

14 6
6 4 3 3 2 1
-1

Izlaz

16

Objašnjenje primera

Niz A će nakon svih operacija izgledati ovako:

1 5 1 5 1 5 1 5 1 5 2 2 5 1

Ograničenja

U svim test primerima važi: MN,109C109

  • U test primerima vrednim 10 poena: N,M10000.
  • U test primerima vrednim 60 poena: N500.000,M100.000.
  • U test primerima vrednim 30 poena: N20.000.000,M100.000.
Autor Tekst i test primeri Analiza rеšenja Testiranje
Ivan Stošić Nikola Spasić Nikola Spasić Aleksa Milisavljević

Označimo skup {1,2,,N} sa [[N]]. Posmatrajmo dodele vrednosti za i=0 i definišimo dve funkcije

  • f:[[N]][[N]], takva da je aj=f(aj), gde je aj vrednost na poziciji j u nizu A nakon izvršavanja dodela vrednosti za i=0 a aj je ta vrednost pre izvršavanja dodela.
  • g:[[N]][[N]],g(N):=1;g(x):=x+1,xN ciklično pomeranje za jedno mesto ulevo.

Sada ćemo operaciju koju treba izvršiti na nizu A (za svako i{0,1,,NM} i za svako j{0,1,,M1}) označiti sa z, gde je z:[[N]][[N]]. Označimo sa zi:[[N]][[N]] operaciju na nizu A koja odgovara izvršavanju unutrašnje for-petlje (po j) za dato fiksno i. Jasno je da važi: z=zNMzNM1z0. Ključna ideja je to što zi možemo zapisati u obliku zi=gifgi. Ovo važi zato što preslikavanjem gi "rotiramo" niz u takvu poziciju da element koji je bio na poziciji i je sada na poziciji 0. Nakon toga primenjujemo dodelu vrednosti sa f i ponovo rotiramo niz u kontra smeru za i mesta.

Ako zatim proširimo izraz za z i skratimo susedne parove gig(i1) ostaje nam z=g(NM)(fg)NMf, odnosno, kraće z=gk(gf)k, gde smo uveli oznaku k:=NM+1. Kako se gk lako računa kao desna rotacija za k mesta, ako uvedemo oznaku h:=gf ostaje nam da izračunamo k-ti stepen ove funkcije.

Da bismo za svako i pronašli vrednost hk(i) konstruišemo graf sa čvorovima {1N} i usmerenim granama {xh(x),x[[N]]}. Kako je usmeren, konačan i iz svakog čvora izlazi tačno jedna grana, komponente povezanosti (posmatrajući graf kao neusmeren) izgledaju kao ciklusi sa stablima čiji je tačno jedan čvor deo ciklusa (primetiti da kako je moguće da h(i)=i postoji mogućnost da ciklus sadrži samo jedan čvor). Usmerenje grana u ciklusu mora biti takvo da je ciklus i u usmerenom grafu, dok usmerenje grana u stablima mora biti takvo da je ulazni čvor bliži ciklusu od izlaznog.

Obilazeći svaku komponentu povezanosti (posmatrajući graf kao neusmeren) u njoj pronalazimo ciklus za čiji svaki čvor nalazimo hk(i) kao c((id(i)+k)modd) gde je d dužina ciklusa koji sadrži čvor i, idx(x) daje redni broj čvora x u ciklusu dok c(x) označava x-ti čvor na tom ciklusu. Za "početak" ciklusa možemo izabrati proizvoljan čvor. Zatim dfs obilaskom, kroz stabla povezana sa tim ciklusom, unazad (suprotno usmerenju grana) za svaki čvor i čiji je otac u stablu obilaska oi računamo hk(i) ili kao prethodnika čvora hk(oi) u ciklusu ukoliko je i na rastojanju od ciklusa manjem od k ili kao sina čvora hk(o(i)) u stablu obilaska u čijem podstablu se nalazi čvor i. Ovog sina možemo naći tako što održavamo niz koji odgovara steku čvorova tokom dfs-a i uzimanjem k-tog prethodnika trenutnog čvora.

Na kraju potrebno je samo izračunati traženu vrednost, znajući vrednosti niza A nakon svih dodela vrednosti.

  • Vremenska složenost algoritma O(n)
  • Prostorna složenost algoritma O(n)
05_vrednost_dodele.cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define mp make_pair
#define ldb long double
#define mt make_tuple
const int N=100050;
const int L=25;
const int mod=998244353;
const int inf=1e9+7;
int n,m,b[N],c,sol,bsol;
void input()
{
    scanf("%i %i",&n,&m);
    for(int i=1;i<=m;i++) scanf("%i",&b[i]);
    scanf("%i",&c);
    c%=mod;
    if(c<0) c+=mod;
}
void output()
{
    printf("%i\n",sol);
}
int par[N][L],a[N],mx[N];
void Solve()
{
    for(int i=1;i<=m;i++) a[i]=i;
    for(int j=1;j<=m;j++)
        a[b[j]]=a[j];
    for(int i=1;i<=m;i++) par[i-1][0]=a[i];
    par[m][0]=-1;
    for(int j=1;j<L;j++) for(int i=0;i<=m;i++)
    {
        if(par[i][j-1]==-1) par[i][j]=-1;
        else par[i][j]=par[par[i][j-1]][j-1];
    }
    for(int i=0;i<=m;i++)
    {
        if(par[i][L-1]!=-1) mx[i]=inf;
        else
        {
            mx[i]=0;
            int u=i;
            for(int j=L-1;~j;j--) if(par[u][j]!=-1) u=par[u][j],mx[i]+=1<<j;
            mx[i]++;
        }
    }
    sol=0;
    int mul=c;
    int zero=0;
    for(int i=1;i<=n;i++)
    {
        int pos;
        if(zero!=-1) zero=par[zero][0];
        if(i+m>n)
        {
            int po=n-m+1;
            pos=i-(n-m+1);
            if(mx[pos]>po)
            {
                for(int k=0;k<L;k++) if((po>>k)&1) pos=par[pos][k];
            }
            else
            {
                pos=po-mx[pos]+m+1;
            }
        }
        else
        {
            int po=i;
            pos=0;
            if(mx[pos]>po)
            {
                pos=zero;
                //for(int k=0;k<L;k++) if((po>>k)&1) pos=par[pos][k];
            }
            else
            {
                pos=po-mx[pos]+m+1;
            }
        }
        sol+=(ll)mul*pos%mod;
        if(sol>=mod) sol-=mod;
        mul=(ll)mul*c%mod;
        //printf("%i ",pos);
    }
    //printf("\n");
}
void Run()
{
    input();
    Solve();
    output();
}

int main()
{
    Run();
    return 0;
}