Należy odpowiedzieć na
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
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); } }
W podzadaniu 2 każde zapytanie dotyczy jednego pola.
Niech
Tak więc złożoność czasowa to
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; }
Kartkę papieru z usuniętą środkową kratką dzielimy na cztery ćwiartki.
Ćwiartka 0 zawiera liczby
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
Oznaczmy przez
Załóżmy, że prostokąt ma rozmiar
Oznaczmy jeszcze
W przypadku
Wzory te pozwalają rozwiązać podzadanie 3 w czasie
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); } }
Aby uzyskać szybsze rozwiązanie, rozwiniemy sumy w tych wzorach. Skorzystamy z ogólnej reguły sumowania
wartości wielomianu w kolejnych punktach:
Grupując współczynniki we wzorach na
Zatem sumy dla prostych kawałków i kawałków z zakrętem są następujące:
Ostatecznie daje to wzory pozwalające rozwiązać zadanie w czasie stałym:
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; }