GRIDEVEN - Editorial

PROBLEM LINK:

Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4

Author:
Tester: sushil2006
Editorialist: iceknight1093

DIFFICULTY:

Easy

PREREQUISITES:

None

PROBLEM:

Given an even integer N, find any N\times N grid such that:

  • Every integer from 1 to N appears exactly N times.
  • Let S_1 denote the sum of elements on the main diagonal, and S_2 the sum of elements on the antidiagonal.
    Then, the sum of every two adjacent rows should equal S_1 + S_2. The same applies to any two adjacent columns.

EXPLANATION:

Let’s start with the idea behind the construction to the odd version, and analyze what changes.

The base idea was to choose some permutation as the first row, then set its rotations as the rows - this automatically ensures that all row and column sums are equal, and that all elements along the main diagonal are the same.
However, now that N is even, there is one major change: the antidiagonal is no longer a permutation.

In fact, it can be observed that if the permutation chosen for the first row is P_1, P_2, \ldots, P_N then the antidiagonal contains exactly the elements
P_N, P_{N-2}, P_{N-4}, \ldots, P_2
repeated twice.

So, our job is now to choose P in such a way that

N\cdot P_1 + 2\cdot (P_2 + P_4 + \cdots + P_N) = N\cdot (N+1)

One way to do that is as follows:
Choose P_1 = \frac{N}{2}

This then means we want 2\cdot (P_2 + P_4 + \cdots + P_N) to equal N\cdot (N+1) - \frac{N^2}{2} = \frac{N\cdot (N+2)}{2}

Now, it can be seen that 2 + 4 + 6 + \ldots + N = 2\cdot (1 + 2 + \ldots + \frac{N}{2}) = \frac{N}{2} \cdot (\frac{N}{2} + 1) = \frac{N\cdot (N+2)}{4}
So, quite conveniently for us, choosing P_2, P_4, \ldots, P_N to be the even numbers gives us exactly what we want!

However, there’s one potential issue: we forced P_1 = \frac{N}{2}, but it’s entirely possible for \frac{N}{2} to be even in which case we’d need it at an even position and it can’t actually be P_1.
However, this is easy to fix: we only need to adjust the sum of P_2 + \cdots + P_N while ensuring that \frac{N}{2} isn’t used.
This can be done by, for example, using \frac{N}{2} + 1 in place of \frac{N}{2}, and then using 1 in place of 2 so that the sum remains the same.


As examples, let’s look at N = 6 and N = 8.

For N = 6, there’s no issue: we choose P_1 = \frac{N}{2} = 3 and then choose all the even numbers at the even positions of P.
For example, consider P = [3, 2, 1, 4, 5, 6].
This gives the grid

\begin{bmatrix} 3 & 2 & 1 & 4 & 5 & 6 \\ 6 & 3 & 2 & 1 & 4 & 5 \\ 5 & 6 & 3 & 2 & 1 & 4 \\ 4 & 5 & 6 & 3 & 2 & 1 \\ 1 & 4 & 5 & 6 & 3 & 2 \\ 2 & 1 & 4 & 5 & 6 & 3 \end{bmatrix}

For N = 8, \frac{N}{2} = 4 is even so we need the adjustment described: instead of choosing all the even numbers at the even positions of P, replace 2 by 1 and \frac{N}{2} by \frac{N}{2} + 1.
For example, let P = [4, 1, 2, 5, 3, 6, 7, 8].
This gives the grid

\begin{bmatrix} 4 & 1 & 2 & 5 & 3 & 6 & 7 & 8 \\ 8 & 4 & 1 & 2 & 5 & 3 & 6 & 7 \\ 7 & 8 & 4 & 1 & 2 & 5 & 3 & 6 \\ 6 & 7 & 8 & 4 & 1 & 2 & 5 & 3 \\ 3 & 6 & 7 & 8 & 4 & 1 & 2 & 5 \\ 5 & 3 & 6 & 7 & 8 & 4 & 1 & 2 \\ 2 & 5 & 3 & 6 & 7 & 8 & 4 & 1 \\ 1 & 2 & 5 & 3 & 6 & 7 & 8 & 4 \end{bmatrix}

There are many other constructions possible, likely simpler to describe than this one. Some implementations can be found below.

TIME COMPLEXITY:

\mathcal{O}(N^ 2) per testcase.

CODE:

Author's code (C++)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define fast                       \
 ios_base::sync_with_stdio(0);      \
 cin.tie(0);                         \
 cout.tie(0);
 
int main(){
    fast;
    ll t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        vector<vector<int>> v(n,vector<int>(n));
        for(int i=0;i<n;i++){
            int ind=(2*i)%n;
            for(int j=0;j<n;j++){
                v[i][(ind-j+n)%n]=j;
            }
        }
        for(auto i:v){
            for(auto j:i) cout<<j+1<<" ";
            cout<<"\n";
        }
    }
}
Tester's code (C++)
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

template<typename T> using Tree = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
typedef long long int ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

#define fastio ios_base::sync_with_stdio(false); cin.tie(NULL)
#define pb push_back
#define endl '\n'
#define sz(a) (int)a.size()
#define setbits(x) __builtin_popcountll(x)
#define ff first
#define ss second
#define conts continue
#define ceil2(x,y) ((x+y-1)/(y))
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
#define yes cout << "YES" << endl
#define no cout << "NO" << endl

#define rep(i,n) for(int i = 0; i < n; ++i)
#define rep1(i,n) for(int i = 1; i <= n; ++i)
#define rev(i,s,e) for(int i = s; i >= e; --i)
#define trav(i,a) for(auto &i : a)

template<typename T>
void amin(T &a, T b) {
    a = min(a,b);
}

template<typename T>
void amax(T &a, T b) {
    a = max(a,b);
}

#ifdef LOCAL
#include "debug.h"
#else
#define debug(...) 42
#endif

/*



*/

const int MOD = 1e9 + 7;
const int N = 1e5 + 5;
const int inf1 = int(1e9) + 5;
const ll inf2 = ll(1e18) + 5;

void solve(int test_case){
    ll n; cin >> n;
    ll a[n][n];
    memset(a,-1,sizeof a);

    if(n&1){
        ll s = 0;
        rep1(x,n){
            if(x > 1) s = (s+2)%n;
            ll j = s;
            rep(i,n){
                assert(a[i][j] == -1);
                a[i][j] = x;
                j = (j-1+n)%n;  
            }
        }
    }
    else{
        ll s = n/2-1;
        ll dir = -1;
        if((n/2)&1) dir = 1;

        rep1(x,n){
            if(x > 1){
                s = (s-1+n)%n;
                dir = -dir;
            }

            ll j = s;
            rep(i,n){
                assert(a[i][j] == -1);
                a[i][j] = x;
                j = (j+dir+n)%n;
            }
        }
    }

    rep(i,n){
        rep(j,n){
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
}

int main()
{
    fastio;

    int t = 1;
    cin >> t;

    rep1(i, t) {
        solve(i);
    }

    return 0;
}
Editorialist's code (PyPy3)
for _ in range(int(input())):
    n = int(input())
    
    grid = [ [0 for _ in range(n)] for _ in range(n) ]
    if n%2 == 0:
        for i in range(n//2):
            for j in range(n//2):
                grid[i][j] = grid[i+n//2][j+n//2] = (j-i)%(n//2) + 1
            for j in range(n//2):
                grid[i][j+n//2] = grid[i+n//2][j] = (i+j)%(n//2) + n//2 + 1
    else:
        for i in range(n):
            for j in range(n):
                grid[i][j] = (n//2 + 1 + i + j)%n + 1
    for row in grid: print(*row)

1 Like

i simply alternated between printing 1 to N and N to 1 in each row. much simpler imo

7 Likes

Is it possible to fix this line of thinking ?

For Even number, if N = 6 , then take the two centre most elements, and put them on each of the diagonals. I had used certain maths, and we can prove that having two center-most values will give you desired diagonal and anti-diagonal sums…

\begin{bmatrix} 3 & 0 & 0 & 0 & 0 & 4 \\ 0 & 3 & 0 & 0 & 4 & 0 \\ 0 & 0 & 3 & 4 & 0 & 0 \\ 0 & 0 & 4 & 3 & 0 & 0 \\ 0 & 4 & 0 & 0 & 3 & 0 \\ 4 & 0 & 0 & 0 & 0 & 3 \\ \end{bmatrix}

And then try to generate permutation of 1 to 6 in each row… I tried a lot, but couldn’t :frowning: .
Wasted more than an hour… If I was fixing row, columns were distorted and vice-versa :frowning: .

Is it even possible to solve it this way ?

1 Like

I divided the square into 4 small squares and filled them with different diagonal patterns, code. It looks like this for N = 6:

\begin{bmatrix} \begin{matrix} 1 & 3 & 2 \\ 2 & 1 & 3 \\ 3 & 2 & 1 \end{matrix} & & \begin{matrix} 5 & 6 & 4 \\ 6 & 4 & 5 \\ 4 & 5 & 6 \end{matrix} \\ \\ \begin{matrix} 4 & 5 & 6 \\ 5 & 6 & 4 \\ 6 & 4 & 5 \end{matrix} & & \begin{matrix} 3 & 2 & 1 \\ 1 & 3 & 2 \\ 2 & 1 & 3 \end{matrix} \end{bmatrix}
2 Likes

beautiful solution. Wish I had thought of it…

Yes, actually I did it this way.

Code
void solve() {
    int n; cin >> n;
    vector<vector<int>> a(n, vector<int>(n, -1));
    for (int i = 0; i < n; i++) {
        a[i][i] = n / 2 + 1;
        a[i][n-i-1] = n / 2;
    }
    vector<int> v;
    for (int i = 1; i <= n; i++) {
        if (i != n / 2 + 1 && i != n / 2) v.pb(i);
    }
    for (int i = 0; i < n / 2; i++) {
        int idx = i % max(1LL, (int)v.size());
        for (int j = 0; j < n; j++) {
            if (a[i][j] == -1) a[i][j] = v[idx], idx = (idx + 1) % v.size();
        }
    }
    for (int i = 0; i < v.size() / 2; i++) {
        swap(v[i], v[v.size() - i - 1]);
    }
    for (int i = n - 1; i >= n / 2; i--) {
        int idx = (n - 1 - i) % max(1LL, (int)v.size());
        for (int j = 0; j < n; j++) {
            if (a[i][j] == -1) a[i][j] = v[idx], idx = (idx + 1) % v.size();
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
}
1 Like

i can’t view your sol , it’s showing like 403 access denied

For the first n/2 rows just print from 1 to n (1,2,3,…n) then for remaining n/2 rows print from n to 1 (n,n-1,n-2…1)! As simple as that :slight_smile:

@iceknight1093 @sushil2006
Regarding the solution for GRIDEVEN , I don’t know but my wrong code still gets accepted even if it doesn’t satisfy all the conditions.

while (t--) {
        int n;
        scanf("%d", &n);
        int arr[n][n];
	    for(int i = 0; i < n; i++)
	    {
	        int cnt = 1, cntt = n;
	        for(int j = 0; j < n; j++)
	        {
	            if(i % 2 == 0)
	            arr[i][j] = cnt++;
	            else 
	            arr[i][j] = cntt--;
	        }
	    }
	    for(int i=0;i<n;i++)
	    {
	        for(int j=0;j<n;j++)
	        {
	            printf("%d ",arr[i][j]);
	        }
	        printf("\n");
	    }
    }

Issue with this approach is for the input

1
5

The output I get is

image

It can clearly be seen that
S1 = 15
S2 = 15
So sum = 30

The sum of column 1 and column 2 is 27 which is not equal to 30.

But my solution was accepted regardless.

Regards
Apoorv :cowboy_hat_face:

This version of the problem has N always even, and your construction does work for that.
GRIDODD is the version where N is odd, and you should get WA if you submit to that.

I am so sorry for my oversight on the constraints. Thank you for the promptly reply and sorry once again.