Opis
BOI 2016
Bosses
Park
Spiral
Cities
Maze
Swap
CEOI 2017
Skierowanie dróg
Pewny zakład
Pułapka Mariusza
Budujemy mosty
Pościg Mariusza
Palindromiczny podział
BOI 2018
Miłosny wielokąt
Marsjańskie DNA
Problemy dżdżownicy
Prąd przemienny
Genetyka
Ścieżki
BOI 2019
Flash Memory
Nautilus
Alpine Valley
Tom's Kitchen
Necklace
Olympiads
BOI 2020
Kolory
Mieszanki
Joker
Graf
Wioska
Wirusy
CEOI 2020
Fajny płot
Drogi
Star Trek
Napój Wielkiej Potęgi
Wiosenne porządki
Szachowa gorączka

Spiral

Na siatce o wymiarach $(2n+1) \times (2n+1)$ narysowano spiralę, wpisując w nią kolejne liczby od 1 (w środkowym polu) do $(2n+1)^2$.

Należy odpowiedzieć na $q$ zapytań, każde o sumę liczb z pól znajdujących się w prostokącie, którego przeciwległe wierzchołki są w polach $(x_1,y_1)$ oraz $(x_2,y_2)$.

Rozwiązanie brutalne

Podzadanie 1 możemy rozwiązać, rysując całą spiralę w tablicy dwuwymiarowej. Następnie stosujemy dwuwymiarowe sumy prefiksowe, które pozwalają obliczać sumę liczb w dowolnym prostokącie zawartym w tablicy w czasie stałym.

Złożoność czasowa tego rozwiązania to $O(n^2 + q)$:

const int N = 1100, MOD = 1000000007;
int n,q;
int b[2*N+1][2*N+1];
int dp[2*N+2][2*N+2];

int main() {
  scanf("%d%d",&n,&q);

  int x=n, y=n, dist=1, k=1;
  b[y][x++] = k++;
  REP(i,n) {
    REP(j,dist) { b[y++][x] = k++; }
    REP(j,dist+1) { b[y][x--] = k++; }
    REP(j,dist+1) { b[y--][x] = k++; }
    REP(j,dist+2) { b[y][x++] = k++; }
    dist += 2;
  }

  REP(y,2*n+1) REP(x,2*n+1) {
    dp[y+1][x+1] = (ll(dp[y+1][x]) + dp[y][x+1]
        + MOD - dp[y][x] + b[y][x]) % MOD;
  }

  for (int i=0; i<q; ++i) {
    int x1,y1,x2,y2;
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    x1 += n; y1 += n; x2 += n; y2 += n;
    x2++; y2++;
    int ans = (ll(dp[y2][x2]) + MOD - dp[y1][x2]
        + MOD - dp[y2][x1] + dp[y1][x1]) % MOD;
    printf("%d\n",ans);
  }
}

Jedno pole

W podzadaniu 2 każde zapytanie dotyczy jednego pola. Niech $m=\max(|x_1|,|y_1|)$, czyli jest to minimalny rozmiar kwadratu (zaczepionego w środkowym polu), w którym znajduje się szukane pole. Zatem w środku tego kwadratu znajduje się $(2m-1)^2$ pól, a szukane pole jest na jednym z czterech boków. Wystarczy zatem odpowiednio rozważyć cztery przypadki, aby uzyskać odpowiedź na zapytanie w czasie stałym.

Tak więc złożoność czasowa to $O(q)$:

int calculate(int x, int y) {
  ll m = max(abs(x), abs(y));
  if (m == 0) { return 1; }
  ll s = (2*m-1) * (2*m-1) % MOD;
  ll ans;
  if (y == -m) {
    ans = s+6*m + x+m;
  } else if (x == -m) {
    ans = s+4*m + -y+m;
  } else if (y == m) {
    ans = s+2*m + -x+m;
  } else {
    ans = s+1 + y+m-1;
  }
  return ans % MOD;
}

Rozwiązanie w czasie liniowym

Kartkę papieru z usuniętą środkową kratką dzielimy na cztery ćwiartki. Ćwiartka 0 zawiera liczby $2, 11, 28\ldots$ oraz liczby powyżej nich, ćwiartka 1 zawiera liczby $4, 15, 34\ldots$ oraz te na lewo, ćwiartka 2 zawiera liczby $6, 19, 40\ldots$ oraz te poniżej, a ćwiartka 3 zawiera liczby $8, 23, 46\ldots$ oraz te na prawo. Powiemy, że powyżej wymienione liczby leżą na głównym wierszu ćwiartki (zacienionym na poniższym rysunku):

Teraz możemy rozwiązać zadanie niezależnie dla każdej ćwiartki (rozpatrując przecięcie prostokąta z daną ćwiartką) i zsumować uzyskane wyniki (plus uwzględniając ewentualny wkład ze środkowego pola z liczbą 1). Dodatkowo możemy założyć, że prostokąt zawiera kratkę z najmniejszą liczbą w ćwiartce (w przeciwnym wypadku to kwestia prostych operacji arytmetycznych na wkładzie z czterech takich prostokątów).

W rozwiązaniu będziemy obliczać sumy potęg kolejnych liczb naturalnych. Oznaczmy to ogólnie przez \begin{equation*} P_r(m) = \sum_{1\leq i\leq m} i^r, \end{equation*} a w szczególności przydadzą się nam wzory pozwalające obliczać je w czasie stałym: \begin{equation*} P_1(m) = \frac12 m(m+1),\qquad P_2(m) = \frac16 m(m+1)(2m+1),\qquad P_3(m) = \frac14 m^2(m+1)^2. \end{equation*}

Oznaczmy przez $A(k,i)$ (dla $0\leq k\leq 3$, $0\leq i$) $i$-tą liczbę w głównym wierszu ćwiartki o numerze $k$. Wypisując sobie ciągi różnicowe dla tych wierszy, możemy zgadnąć następujący wzór: \begin{equation*} A(k,i) = 1 + (2k+1)(i+1) + 8P_1(i). \end{equation*}

Załóżmy, że prostokąt ma rozmiar $h\times w$ kratek (dla ćwiartki 0 to oznacza prostokąt o wysokości $h$ i szerokości $w$, dla każdej kolejnej ćwiartki jest on obrócony o $90^\circ$). Rozważmy najpierw przypadek $w \geq h-1$. Wtedy spiralę zawartą w prostokącie można podzielić na $w$ kawałków, z których każdy zaczyna się liczbą z głównego wiersza i zawiera kolejne liczby naturalne (więc jest opisany początkową liczbą i swoją długością). Oznaczmy przez $S(k,i,\ell)$ sumę liczb w kawałku zaczynającym się $i$-tą liczbą o długości $\ell$. Wtedy \begin{equation*} S(k,i,\ell) = A(k,i) \cdot \ell + P_1(\ell-1). \end{equation*}

Oznaczmy jeszcze $\gamma_k = [k=3]$ (nawias Iversona). Pierwsze $h-1-\gamma_k$ kawałków mają po jednym zakręcie, a $i$-ty z nich ma długość $2i+2+\gamma_k$. Pozostałe kawałki są proste i mają długość $h$. Więc suma liczb w prostokącie wynosi \begin{equation*} S_{\geq}\ =\ \sum_{0\leq i < h-1-\gamma_k} S(k,i,2i+2+\gamma_k)\ +\ \sum_{h-1-\gamma_k\leq i < w} S(k,i,h). \end{equation*}

W przypadku $w < h-1$ jest trochę trudniej. Mamy $w$ kawałków z zakrętem, a dodatkowo $h-1-w-\gamma_k$ kawałków poziomych o długości $w$, z tym że nie zaczynają się one liczbami z głównego wiersza ćwiartki. Można jednak zrobić następujący trik: ponieważ kończą się one liczbą tuż przed głównym wierszem następnej ćwiartki, można od tej liczby policzyć $w$ liczb poprzednich. Da to następujący wzór: \begin{equation*} S_{<}\ =\ \sum_{0\leq i<w} S(k,i,2i+2+\gamma_k)\ +\ \sum_{w+\gamma_k\leq i<h-1} -S((k+1)\bmod 4, i, -w). \end{equation*}

Wzory te pozwalają rozwiązać podzadanie 3 w czasie $O(n)$:

const int M = 1000000007;
int n,x1,Y1,x2,y2;
ll sum;

ll P1(ll m) {
  if (m % 2 == 0) return ((m/2) * (m+1)) % M;
  return (m * ((m+1)/2)) % M;
}

ll S(ll k, ll i, ll l) {
  ll r = 2*M;
  r = (r + 2*k+2) % M;
  r = (r + i * (2*k+5)) % M;
  r = (r + 4*i*i) % M;
  r = (r * l) % M;
  r = (r + P1(l-1)) % M;
  return r;
}

ll rog_cwiartka(ll k, ll w, ll h) {
  ll sum = 2*M;
  int g = k==3;

  if (w >= h-1) {
    for(int i=0;i<h-1-g;++i) sum += S(k,i,2*i+2+g);
    for(int i=h-1-g;i<w;++i) sum += S(k,i,h);
  } else {
    for(int i=0;i<w;++i) sum += S(k,i,2*i+2+g);
    for(int i=w+g;i<h-1;++i) sum += -S((k+1)%4,i,-w);
  }

  return sum % M;
}

ll cwiartka(int k, int x1, int Y1, int x2, int y2) {
  return rog_cwiartka(k, x2, y2) - rog_cwiartka(k, x2, Y1)
    - rog_cwiartka(k, x1, y2) + rog_cwiartka(k, x1, Y1);
}

void obroc(int& x1, int& Y1, int& x2, int& y2) {
  int nx1 = Y1, nY1 = -x2, nx2 = y2, ny2 = -x1;
  x1 = nx1; Y1 = nY1; x2 = nx2; y2 = ny2;
}

int main() {
  int q;
  scanf("%d",&n);
  scanf("%d", &q);
  while (q--) {
    scanf("%d%d%d%d",&x1,&Y1,&x2,&y2);
  
    sum = 2*M + int(x1 <= 0 && 0 <= x2 && Y1 <= 0 && 0 <= y2);
  
    for (int k=0; k<4; ++k) {
      sum += cwiartka(k, max(1,x1)-1, max(0,Y1), 
          max(1,x2+1)-1, max(0,y2+1));
      obroc(x1, Y1, x2, y2);
    }
  
    printf("%lld\n",sum % M);
  }
}

Rozwiązanie wzorcowe

Aby uzyskać szybsze rozwiązanie, rozwiniemy sumy w tych wzorach. Skorzystamy z ogólnej reguły sumowania wartości wielomianu w kolejnych punktach: \begin{equation*} \sum_{0\leq i<m} (a_0 + a_1 i + a_2 i^2 + a_3 i^3) = a_0 m + a_1 P_1(m-1) + a_2 P_2(m-1) + a_3 P_3(m-1). \end{equation*}

Grupując współczynniki we wzorach na $A$ i $S$ względem potęg $i$, dostajemy: \begin{equation*} \begin{split} A(k,i) &= (2k+2) + (2k+5)i + 4i^2, \\ S(k,i,\ell) &= (2k+2)\ell + P_1(\ell-1) + (2k+5)\ell i + 4\ell i^2, \\ S(k,i,2i+2+\gamma_k) &= \frac12(2+\gamma_k)(2k+5+\gamma_k) + (8k+2k\gamma_k+7\gamma_k+17)i + 4(k+\gamma_k+5)i^2 + 8i^3. \end{split} \end{equation*}

Zatem sumy dla prostych kawałków i kawałków z zakrętem są następujące: \begin{equation*} \begin{split} T_P(k,m,\ell) &= \sum_{0\leq i<m} S(k,i,\ell) = ((2k+2)\ell + P_1(\ell-1)) m + (2k+5)\ell P_1(m-1) + 4\ell P_2(m-1), \\ T_Z(k,m) &= \sum_{0\leq i<m} S(k,i,2i+2+\gamma_k) = {} \\ &= \frac12(2+\gamma_k)(2k+5+\gamma_k)m + (8k+2k\gamma_k+7\gamma_k+17) P_1(m-1) + {}\\ &\quad\quad\quad{}+ 4(k+\gamma_k+5) P_2(m-1) + 8P_3(m-1). \end{split} \end{equation*}

Ostatecznie daje to wzory pozwalające rozwiązać zadanie w czasie stałym: \begin{equation*} \begin{split} S_{\geq} &= T_Z(k,h-1-\gamma_k) + T_P(k,w,h) - T_P(k,h-1-\gamma_k,h), \\ S_{<} &= T_Z(k,w) - T_P((k+1)\bmod 4,h-1,-w) + T_P((k+1)\bmod 4,w+\gamma_k,-w). \end{split} \end{equation*}

ll A(ll a, ll b) { a %= M; b %= M; return (a+b) % M; }
ll B(ll a, ll b) { a %= M; b %= M; return (a*b) % M; }

ll oblicz(vector<ll> mnoz, vector<int> p_dziel) {
  for (int i = 0; i < (int)p_dziel.size(); i++) {
    bool ok = false;
    for (int j = 0; j < (int)mnoz.size(); j++)
      if (mnoz[j] % p_dziel[i] == 0) {
        mnoz[j] /= p_dziel[i];
        ok = true;
        break;
      }
    assert(ok);
  }

  ll r = 1;
  for (int i = 0; i < (int)mnoz.size(); i++)
    r = B(r, mnoz[i]);
  return r;
}

ll P1(ll m) { return oblicz({m, m+1}, {2}); }
ll P2(ll m) { return oblicz({m, m+1, 2*m+1}, {2, 3}); }
ll P3(ll m) { return oblicz({m, m, m+1, m+1}, {2, 2}); }

ll TP(ll k, ll m, ll l) {
  return (B(A(B(2*k+2, l), P1(l-1)), m) + B(B(2*k+5, l), P1(m-1))
      + B(B(4, l), P2(m-1))) % M;
}

ll TZ(ll k, ll m, ll g) {
  return (oblicz({4*k+5+g, 2+g, m}, {2})
        + B(A(A(A(B(8, k), B(2, B(k, g))), B(7, g)), 17), P1(m-1))
        + B(B(4, g+k+5), P2(m-1))
        + B(8, P3(m-1))) % M;
}

ll rog_cwiartka(int k, int w, int h) {
  ll sum = 0;
  int g = k==3;

  if (w >= h-1) {
    sum = (TZ(k,h-1-g,g) + TP(k,w,h) - TP(k,h-1-g,h) + M) % M;
  } else {
    sum = (TZ(k,w,g) - TP((k+1)%4,h-1,-w) + TP((k+1)%4,w+g,-w) + M) % M;
  }

  return sum;
}