import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565B {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] maxCards = new int[N][5];

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(maxCards, i, cost, damage);
			}
		}

		long[] damage1 = new long[3*N+1];
		long[] damage2 = new long[3*N+1];

		for(int i = 0; i < N; i++) {	//split addOn
			long[][] addOn = new long[2][4];;
			addOn[0] = new long[] {0, max1(maxCards[i]), max2Sum(maxCards[i]), max3Sum(maxCards[i])};
			addOn[1] = new long[] {0, max1B(maxCards[i]), max2SumB(maxCards[i]), max3SumB(maxCards[i])};
	//		System.out.println("addOn:");
	//		System.out.println(Arrays.toString(addOn[0]));
	//		System.out.println(Arrays.toString(addOn[1]));
			for(int j = 0; j <= 3*i; j++) {
				for(int k = 0; k < 4; k++) {
					int multiplier = ((j+k) % 10 == 0 && j > 0) ? 2 : 1;
	//				if(multiplier == 2)
	//					System.out.println("WOOH " + i + ", " + j + ", " + k);
					if(addOn[multiplier-1][k] != -1) {
						damage2[j+k] = Math.max(damage1[j] + addOn[multiplier-1][k], damage2[j+k]);
					}
					else {
						damage2[j+k] = 0;
					}

				}
			}

	//		System.out.println("damage2: " + i);
	//		System.out.println(Arrays.toString(damage2));

			long[] tempDamage = damage1;
			damage1 = damage2;
			damage2 = tempDamage;

		}

		long max = max(damage1);
		System.out.println(max);
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max3SumB(int[] a) {
		long sum = a[2];
		sum *= 2;
		sum += a[0] + a[1];

		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return sum;
	}

	public static long max2Sum(int[] a) {
		long ans = a[2];
		ans += Math.max(a[1], a[3]);
		if(a[2] == 0 || Math.max(a[1], a[3]) == 0) {
			return -1;
		}

		return ans;
	}

	public static long max2SumB(int[] a) {
		long[] b = {a[1], a[2], a[3]};
		Arrays.sort(b);

		if(b[2] == 0 || b[1] == 0)
			return -1;

		return 2*b[2] + b[1];
	}

	public static long max(long[] a) {
		long max = 0;
		for(long i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1B(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return 2 * max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2588

4
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
1
2 1000

2009
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565B {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] maxCards = new int[N][5];

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(maxCards, i, cost, damage);
			}
		}

		long[] damage1 = new long[3*N+1];
		long[] damage2 = new long[3*N+1];

		Arrays.fill(damage1, Integer.MIN_VALUE);
		Arrays.fill(damage2, Integer.MIN_VALUE);
		damage1[0] = 0;

		for(int i = 0; i < N; i++) {

			long[][] arr = new long[4][2];
			arr[0] = new long[] {0, max1(maxCards[i]), max2Sum(maxCards[i]), max3Sum(maxCards[i])};
			arr[1] = new long[] {0, max1B(maxCards[i]), max2SumB(maxCards[i]), max3SumB(maxCards[i])};

		//	System.out.println("arr[0]:" + Arrays.toString(arr[0]));
		//	System.out.println("arr[1]:" + Arrays.toString(arr[1]));

			for(int j = 0; j <= 3*i; j++) {

				damage2[j] = Math.max(damage1[j], damage2[j]);

				if((j+1) % 10 != 0) {
					if(damage1[j] >= 0 && arr[0][1] != -1) {
						damage2[j+1] = Math.max(damage1[j] + arr[0][1], damage2[j+1]);
					}
					else {
						damage2[j+1] = Math.max(-1, damage2[j+1]);
					}
				}
				else {
					if(damage1[j] >= 0 && arr[1][1] != -1)
						damage2[j+1] = Math.max(damage1[j] + arr[1][1], damage2[j+1]);
					else {
						damage2[j+1] = Math.max(-1, damage2[j+1]);
					}
				}

				if((j+2) % 10 != 0) {
					if(damage1[j] >= 0 && arr[0][2] != -1)
						damage2[j+2] = Math.max(damage1[j] + arr[0][2], damage2[j+2]);
					else {
						damage2[j+2] = Math.max(-1, damage2[j+2]);
					}
				}
				else {
					if(damage1[j] >= 0 && arr[1][2] != -1)
						damage2[j+2] = Math.max(damage1[j] + arr[1][2], damage2[j+2]);
					else
						damage2[j+2] = Math.max(-1, damage2[j+2]);
				}

				if((j+3) % 10 != 0) {
					if(damage1[j] >= 0 && arr[0][3] != -1)
						damage2[j+3] = Math.max(damage1[j] + arr[0][3], damage2[j+3]);
					else
						damage2[j+3] = Math.max(-1, damage2[j+3]);
				}
				else {
					if(damage1[j] >= 0 && arr[1][3] != -1)
						damage2[j+3] = Math.max(damage1[j] + arr[1][3], damage2[j+3]);
					else
						damage2[j+3] = Math.max(-1, damage2[j+3]);
				}
			}

		//	System.out.println("damage:");
		//	System.out.println(Arrays.toString(damage2));

			long[] temp = damage1;
			damage1 = damage2;
			damage2 = temp;
		}

		long max = max(damage1);
		System.out.println(max);
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max3SumB(int[] a) {
		long sum = a[2];
		sum *= 2;
		sum += a[0] + a[1];

		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return sum;
	}

	public static long max2Sum(int[] a) {
		long ans = a[2];
		ans += Math.max(a[1], a[3]);
		if(a[2] == 0 || Math.max(a[1], a[3]) == 0) {
			return -1;
		}

		return ans;
	}

	public static long max2SumB(int[] a) {
		long[] b = {a[1], a[2], a[3]};
		Arrays.sort(b);

		if(b[2] == 0 || b[1] == 0)
			return -1;

		return 2*b[2] + b[1];
	}

	public static long max(long[] a) {
		long max = 0;
		for(long i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1B(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return 2 * max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2588
2282

4
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
1
2 1000

2009

19
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 200
1
1 100
1
1 100
1
1 200
1
1 100
1
3 1000000

2001800
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565B {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] maxCards = new int[N][5];

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(maxCards, i, cost, damage);
			}
		}

		long[] damage1 = new long[3*N+1];
		long[] damage2 = new long[3*N+1];

		for(int i = 0; i < N; i++) {	//split addOn
			long[][] addOn = new long[2][4];;
			addOn[0] = new long[] {0, max1(maxCards[i]), max2Sum(maxCards[i]), max3Sum(maxCards[i])};
			addOn[1] = new long[] {0, max1B(maxCards[i]), max2SumB(maxCards[i]), max3SumB(maxCards[i])};
	//		System.out.println("addOn:");
	//		System.out.println(Arrays.toString(addOn[0]));
	//		System.out.println(Arrays.toString(addOn[1]));
			for(int j = 0; j <= 3*i; j++) {
				for(int k = 0; k < 4; k++) {
					int multiplier = ((j+k) % 10 == 0 && j > 0) ? 2 : 1;
	//				if(multiplier == 2)
	//					System.out.println("WOOH " + i + ", " + j + ", " + k);
					if(damage1[j] != -1 && addOn[multiplier-1][k] != -1) {
						damage2[j+k] = Math.max(damage1[j] + addOn[multiplier-1][k], damage2[j+k]);
					}
					else if(damage2[j+k] == 0){
						damage2[j+k] = -1;
					}

				}
			}

	//		System.out.println("damage2: " + i);
	//		System.out.println(Arrays.toString(damage2));

			long[] tempDamage = damage1;
			damage1 = damage2;
			damage2 = tempDamage;

		}

		long max = max(damage1);
		System.out.println(max);
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max3SumB(int[] a) {
		long sum = a[2];
		sum *= 2;
		sum += a[0] + a[1];

		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return sum;
	}

	public static long max2Sum(int[] a) {
		long ans = a[2];
		ans += Math.max(a[1], a[3]);
		if(a[2] == 0 || Math.max(a[1], a[3]) == 0) {
			return -1;
		}

		return ans;
	}

	public static long max2SumB(int[] a) {
		long[] b = {a[1], a[2], a[3]};
		Arrays.sort(b);

		if(b[2] == 0 || b[1] == 0)
			return -1;

		return 2*b[2] + b[1];
	}

	public static long max(long[] a) {
		long max = 0;
		for(long i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1B(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return 2 * max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2588
2282

4
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
1
2 1000

2009
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.InputMismatchException;
public class F565B {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] maxCards = new int[N][5];

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(maxCards, i, cost, damage);
			}
		}

		long[] damage1 = new long[3*N+1];
		long[] damage2 = new long[3*N+1];

		for(int i = 0; i < N; i++) {
			long[] addOn = {0, max1(maxCards[i]), max2Sum(maxCards[i]), max3Sum(maxCards[i])};
	//		System.out.println("addOn:");
	//		System.out.println(Arrays.toString(addOn));
			for(int j = 0; j <= 3*i; j++) {
				for(int k = 0; k < 4; k++) {
					int multiplier = (j % 10 == 0 && j > 0) ? 2 : 1;
					damage2[j+k] = Math.max(damage1[j] + multiplier * addOn[k], damage2[j+k]);
				}
			}

	//		System.out.println("damage2: " + i);
	//		System.out.println(Arrays.toString(damage2));

			long[] tempDamage = damage1;
			damage1 = damage2;
			damage2 = tempDamage;

		}

		long max = max(damage1);
		System.out.println(max);
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max2Sum(int[] a) {
		return a[2] + Math.max(a[1], a[3]);
	}

	public static long max(long[] a) {
		long max = 0;
		for(long i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2588
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565 {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] turns = new int[N][5];
		long[][][] dp = new long[N+1][10][4];

		for(int i = 0; i < turns.length; i++)
			for(int j = 0; j < 5; j++)
				turns[i][j] = -1;

		for(int i = 0; i < dp.length; i++) {
			for(int j = 0; j < dp[i].length; j++) {
				for(int k = 0; k < dp[i][j].length; k++) {
					dp[i][j][k] = -1;
				}
			}
		}

		dp[0][0][0] = 0;

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(turns, i, cost, damage);
			}
		}

//		System.out.println("turns:");
//		print(turns);

		for(int i = 0; i < dp.length - 1; i++) {
			for(int j = 0; j < dp[i].length; j++) {
				for(int k = 0; k < dp[i][j].length; k++) {
					if(dp[i][j][k] == -1)
						continue;
					for(int c = 0; c < 4; c++) {
						long temp = -1;
						if(j + c >= 10) {
							long addOn = doDamage(turns[i], c, true);
							if(addOn == -1)
								continue;
							temp = dp[i][j][k] + addOn;
						}
						else {
							long addOn = doDamage(turns[i], c, false);
							if(addOn == -1)
								continue;
							temp = dp[i][j][k] + addOn;
						}

						dp[i+1][(j+c) % 10][c] = Math.max(temp, dp[i+1][(j+c) % 10][c]);
					}
				}
			}
		}

		long max = 0;
		for(int j = 0; j < 10; j++) {
			for(int k = 0; k < 4; k++) {
				max = Math.max(dp[N][j][k], max);
			}
		}

//		System.out.println("dp:");
//		print(dp);

		System.out.println(max);
	}

	public static long doDamage(int[] cards, int howMany, boolean doDouble) {
		if(howMany == 0)
			return 0;

		if(!doDouble) {
			if(howMany == 1) {
				return cards[2];
			}
			if(howMany == 2) {
				long temp = Math.max(cards[1], cards[3]);
				if(cards[2] == -1 || temp == -1)
					return -1;
				return temp + cards[2];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;
				long ans = cards[0];
				return ans + cards[1] + cards[2];
			}
		}
		else {
			if(howMany == 1) {
				if(cards[2] == -1) return -1;
				return 2 * cards[2];
			}
			if(howMany == 2) {
				long[] temp = {cards[1], cards[2], cards[3]};
				Arrays.sort(temp);

				if(temp[2] == -1 || temp[1] == -1)
					return -1;

				return temp[2] + temp[1];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;

				long ans = 2 * cards[2];
				return ans + cards[1] + cards[0];
			}
		}

		return -1;
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max2Sum(int[] a) {
		return a[2] + Math.max(a[1], a[3]);
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2323
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565B {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] maxCards = new int[N][5];

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(maxCards, i, cost, damage);
			}
		}

	//	System.out.println("maxCards:");
	//	print(maxCards);

		long[] damage1 = new long[3*N+4];
		long[] damage2 = new long[3*N+4];

		Arrays.fill(damage1, Integer.MIN_VALUE);
		Arrays.fill(damage2, Integer.MIN_VALUE);
		damage1[0] = 0;

		int length = 1;

		for(int i = 0; i < N; i++) {

			long[][] arr = new long[4][2];
			arr[0] = new long[] {0, max1(maxCards[i]), max2Sum(maxCards[i]), max3Sum(maxCards[i])};
			arr[1] = new long[] {0, max1B(maxCards[i]), max2SumB(maxCards[i]), max3SumB(maxCards[i])};

	//		System.out.println("arr[0]:" + Arrays.toString(arr[0]));
	//		System.out.println("arr[1]:" + Arrays.toString(arr[1]));

			for(int j = Math.max(0, length-20); j < length; j++) {

				damage2[j] = Math.max(damage1[j], damage2[j]);

				if((j+1) % 10 != 0) {
					if(damage1[j] >= 0 && arr[0][1] != -1) {
						damage2[j+1] = Math.max(damage1[j] + arr[0][1], damage2[j+1]);
						length = Math.max(j+2, length);
					}
					else {
						damage2[j+1] = Math.max(-1, damage2[j+1]);
					}
				}
				else {
					if(damage1[j] >= 0 && arr[1][1] != -1) {
						damage2[j+1] = Math.max(damage1[j] + arr[1][1], damage2[j+1]);
						length = Math.max(j+2, length);
					}
					else {
						damage2[j+1] = Math.max(-1, damage2[j+1]);
					}
				}

				if((j+2) % 10 != 0 && (j+2) % 10 != 1) {
					if(damage1[j] >= 0 && arr[0][2] != -1) {
						damage2[j+2] = Math.max(damage1[j] + arr[0][2], damage2[j+2]);
						length = Math.max(j+3, length);
					}
					else {
						damage2[j+2] = Math.max(-1, damage2[j+2]);
					}
				}
				else {
					if(damage1[j] >= 0 && arr[1][2] != -1) {
						damage2[j+2] = Math.max(damage1[j] + arr[1][2], damage2[j+2]);
						length = Math.max(j+3, length);
					}
					else
						damage2[j+2] = Math.max(-1, damage2[j+2]);
				}

				if((j+3) % 10 != 0 && (j+3) % 10 != 1 && (j+3) % 10 != 2) {
					if(damage1[j] >= 0 && arr[0][3] != -1) {
						damage2[j+3] = Math.max(damage1[j] + arr[0][3], damage2[j+3]);
						length = Math.max(j+4, length);
					}
					else
						damage2[j+3] = Math.max(-1, damage2[j+3]);
				}
				else {
					if(damage1[j] >= 0 && arr[1][3] != -1) {
						damage2[j+3] = Math.max(damage1[j] + arr[1][3], damage2[j+3]);
						length = Math.max(j+4, length);
					}
					else
						damage2[j+3] = Math.max(-1, damage2[j+3]);
				}
			}

	//		System.out.println("damage:");
	//		System.out.println(Arrays.toString(damage2));

			long[] temp = damage1;
			damage1 = damage2;
			damage2 = temp;
		}

		long max = 0;
		for(int i = 0; i <= 3*N; i++)
			max = Math.max(damage1[i], max);

		System.out.println(max);
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		long sum = a[0];
		sum += a[1];
		sum += a[2];
		return sum;
	}

	public static long max3SumB(int[] a) {
		long sum = a[2];
		sum *= 2;
		sum += a[0];
		sum += a[1];

		if(a[0] == 0 || a[1] == 0 || a[2] == 0) {
			return -1;
		}

		return sum;
	}

	public static long max2Sum(int[] a) {
		long ans = a[2];
		ans += Math.max(a[1], a[3]);
		if(a[2] == 0 || Math.max(a[1], a[3]) == 0) {
			return -1;
		}

		return ans;
	}

	public static long max2SumB(int[] a) {
		long[] b = {a[1], a[2], a[3]};
		Arrays.sort(b);

		if(b[2] == 0 || b[1] == 0)
			return -1;

		return 2*b[2] + b[1];
	}

	public static long max(long[] a) {
		long max = 0;
		for(long i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static long max1B(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return 2 * max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2323

4
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
4
1 1
1 1
1 1
2 100
1
2 1000

2009

19
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 200
1
1 100
1
1 100
1
1 200
1
1 100
1
3 1000000

2001800

19
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 100
1
1 200
1
1 200
1
1 100
1
1 100
1
1 200
1
1 100
1
3 10

2810
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565 {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] turns = new int[N][5];
		long[][][] dp = new long[N+1][10][4];

		for(int i = 0; i < turns.length; i++)
			for(int j = 0; j < 5; j++)
				turns[i][j] = -1;

		for(int i = 0; i < dp.length; i++)
			for(int j = 0; j < dp[i].length; j++)
				for(int k = 0; k < dp[i][j].length; k++)
					dp[i][j][k] = -1;

		dp[0][0][0] = 0;

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++)
				update(turns, i, in.nextInt(), in.nextInt());
		}

		for(int i = 0; i < dp.length - 1; i++) {
			for(int j = 0; j < dp[i].length; j++) {
				for(int k = 0; k < dp[i][j].length; k++) {
					if(dp[i][j][k] == -1)
						continue;

					for(int c = 0; c < 4; c++) {
						long addOn = doDamage(turns[i], c, j+c >= 10);
						if(addOn == -1)
							continue;
						long temp = dp[i][j][k] + addOn;

						dp[i+1][(j+c) % 10][c] = Math.max(temp, dp[i+1][(j+c) % 10][c]);
					}
				}
			}
		}

		long max = 0;
		for(int j = 0; j < 10; j++)
			for(int k = 0; k < 4; k++)
				max = Math.max(dp[N][j][k], max);

		System.out.println(max);
	}

	public static long doDamage(int[] cards, int howMany, boolean doDouble) {
		if(howMany == 0)
			return 0;

		if(!doDouble) {
			if(howMany == 1) {
				long[] arr = {cards[2], cards[3], cards[4]};
				Arrays.sort(arr);
				return arr[2];
			}
			if(howMany == 2) {
				long temp = Math.max(cards[1], cards[3]);
				if(cards[2] == -1 || temp == -1)
					return -1;
				return temp + cards[2];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;
				long ans = cards[0];
				return ans + cards[1] + cards[2];
			}
		}
		else {
			if(howMany == 1) {
				long[] arr = {cards[2], cards[3], cards[4]};
				Arrays.sort(arr);
				if(arr[2] == -1)
					return -1;
				return 2 * arr[2];
			}
			if(howMany == 2) {
				long[] temp = {cards[1], cards[2], cards[3]};
				Arrays.sort(temp);

				if(temp[2] == -1 || temp[1] == -1)
					return -1;

				return 2*temp[2] + temp[1];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;

				long ans = 2 * cards[2];
				return ans + cards[1] + cards[0];
			}
		}

		return -1;
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3])
				maxCards[index][3] = damage;
		}
		else {
			if(damage > maxCards[index][4])
				maxCards[index][4] = damage;
		}
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2323
*/


import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.InputMismatchException;
public class F565 {

	public static void main(String[] args) {
		FastScanner in = new FastScanner(System.in);

		int N = in.nextInt();
		int[][] turns = new int[N][5];
		long[][][] dp = new long[N+1][10][4];

		for(int i = 0; i < turns.length; i++)
			for(int j = 0; j < 5; j++)
				turns[i][j] = -1;

		for(int i = 0; i < dp.length; i++) {
			for(int j = 0; j < dp[i].length; j++) {
				for(int k = 0; k < dp[i][j].length; k++) {
					dp[i][j][k] = -1;
				}
			}
		}

		dp[0][0][0] = 0;

		for(int i = 0; i < N; i++) {
			int numCards = in.nextInt();
			for(int j = 0; j < numCards; j++) {
				int cost = in.nextInt();
				int damage = in.nextInt();
				update(turns, i, cost, damage);
			}
		}

//		System.out.println("turns:");
//		print(turns);

		for(int i = 0; i < dp.length - 1; i++) {
			for(int j = 0; j < dp[i].length; j++) {
				for(int k = 0; k < dp[i][j].length; k++) {
					if(dp[i][j][k] == -1)
						continue;
					for(int c = 0; c < 4; c++) {
						long temp = -1;
						if(j + c >= 10) {
							long addOn = doDamage(turns[i], c, true);
							if(addOn == -1)
								continue;
							temp = dp[i][j][k] + addOn;
						}
						else {
							long addOn = doDamage(turns[i], c, false);
							if(addOn == -1)
								continue;
							temp = dp[i][j][k] + addOn;
						}

						dp[i+1][(j+c) % 10][c] = Math.max(temp, dp[i+1][(j+c) % 10][c]);
					}
				}
			}
		}

		long max = 0;
		for(int j = 0; j < 10; j++) {
			for(int k = 0; k < 4; k++) {
				max = Math.max(dp[N][j][k], max);
			}
		}

//		System.out.println("dp:");
//		print(dp);

		System.out.println(max);
	}

	public static long doDamage(int[] cards, int howMany, boolean doDouble) {
		if(howMany == 0)
			return 0;

		if(!doDouble) {
			if(howMany == 1) {
				long[] arr = {cards[2], cards[3], cards[4]};
				Arrays.sort(arr);
				return arr[2];
			}
			if(howMany == 2) {
				long temp = Math.max(cards[1], cards[3]);
				if(cards[2] == -1 || temp == -1)
					return -1;
				return temp + cards[2];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;
				long ans = cards[0];
				return ans + cards[1] + cards[2];
			}
		}
		else {
			if(howMany == 1) {
				long[] arr = {cards[2], cards[3], cards[4]};
				Arrays.sort(arr);
				if(arr[2] == -1)
					return -1;
				return 2 * arr[2];
			}
			if(howMany == 2) {
				long[] temp = {cards[1], cards[2], cards[3]};
				Arrays.sort(temp);

				if(temp[2] == -1 || temp[1] == -1)
					return -1;

				return 2*temp[2] + temp[1];
			}
			if(howMany == 3) {
				if(cards[0] == -1 || cards[1] == -1 || cards[2] == -1)
					return -1;

				long ans = 2 * cards[2];
				return ans + cards[1] + cards[0];
			}
		}

		return -1;
	}

	public static void update(int[][] maxCards, int index, int cost, int damage) {
		if(cost == 1) {
			if(damage > maxCards[index][0]) {
				maxCards[index][0] = damage;
				if(damage > maxCards[index][1]) {
					maxCards[index][0] = maxCards[index][1];
					maxCards[index][1] = damage;
					if(damage > maxCards[index][2]) {
						maxCards[index][1] = maxCards[index][2];
						maxCards[index][2] = damage;
					}
				}
			}
		}
		else if(cost == 2) {
			if(damage > maxCards[index][3]) {
				maxCards[index][3] = damage;
			}
		}
		else {
			if(damage > maxCards[index][4]) {
				maxCards[index][4] = damage;
			}
		}
	}

	public static long max3Sum(int[] a) {
		return ((long) a[0]) + a[1] + a[2];
	}

	public static long max2Sum(int[] a) {
		return a[2] + Math.max(a[1], a[3]);
	}

	public static long max1(int[] a) {
		long max = 0;
		for(int i : a)
			max = Math.max(i, max);

		return max;
	}

	public static void print(int[][] a) {
		for(int i = 0; i < a.length; i++) {
			for(int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(int[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	public static void print(long[][][] a) {
		for(int i = 0; i < a.length; i++) {
			System.out.println("i = " + i);
			for(int j = 0; j < a[i].length; j++) {
				for(int k = 0; k < a[i][j].length; k++) {
					System.out.print(a[i][j][k] + " ");
				}
				System.out.println();
			}
			System.out.println();
		}
		System.out.println();
	}

	/**
	 * Source: Matt Fontaine
	 */
	static class FastScanner {
		private InputStream stream;
		private byte[] buf = new byte[1024];
		private int curChar;
		private int chars;

		public FastScanner(InputStream stream) {
			this.stream = stream;
		}

		int read() {
			if (chars == -1)
				throw new InputMismatchException();
			if (curChar >= chars) {
				curChar = 0;
				try {
					chars = stream.read(buf);
				} catch (IOException e) {
					throw new InputMismatchException();
				}
				if (chars <= 0)
					return -1;
			}
			return buf[curChar++];
		}

		boolean isSpaceChar(int c) {
			return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
		}

		boolean isEndline(int c) {
			return c == '\n' || c == '\r' || c == -1;
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

		public String next() {
			int c = read();
			while (isSpaceChar(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isSpaceChar(c));
			return res.toString();
		}

		public String nextLine() {
			int c = read();
			while (isEndline(c))
				c = read();
			StringBuilder res = new StringBuilder();
			do {
				res.appendCodePoint(c);
				c = read();
			} while (!isEndline(c));
			return res.toString();
		}
	}

}
/*
5
3
1 6
1 7
1 5
2
1 4
1 3
3
1 10
3 5
2 3
3
1 15
2 4
1 10
1
1 100
outputCopy
263

15
5
1 5
3 10
2 42
2 73
1 5
5
1 38
2 39
3 33
3 33
1 80
5
3 39
1 95
1 18
1 15
2 91
5
3 43
1 85
3 14
3 23
2 80
5
3 3
1 92
3 20
3 29
2 81
5
2 73
1 88
3 27
1 100
1 75
5
2 91
1 77
1 16
3 81
3 87
5
3 14
2 28
3 74
3 96
1 16
5
1 28
1 42
1 3
1 95
2 29
5
3 18
3 78
1 97
1 43
1 84
5
2 6
2 31
3 37
3 7
3 75
5
1 34
1 2
1 6
3 54
3 58
5
3 21
2 89
3 41
2 97
2 1
5
2 29
1 76
1 44
3 29
2 56
5
3 61
3 47
3 4
1 66
1 16

2323
*/


import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner stdin = new Scanner(System.in);
        /*int n = stdin.nextInt();
        for(int i = 0; i < n; i++)
        {
        	test(stdin);
        }*/
        test(stdin);
        stdin.close();
    }
    public static void test(Scanner stdin)
    {
    	int n = stdin.nextInt();
    	long[][] dp = new long[n][10];
    	long max[] = new long[3];
    	for(int i = 0; i< n; i++)
    	{
    		int m = stdin.nextInt();
    		PriorityQueue<Integer>[] list = new PriorityQueue[3];
    		for(int u = 0; u < 3; u++)
    		{
    			list[u] = new PriorityQueue();
    		}
    		for(int u = 0; u < m; u++)
    		{
    			list[stdin.nextInt() - 1].add(-stdin.nextInt());
    		}
    		long a31,a21,a22,a11,a12,a13;
    		if(list[2].isEmpty())
    		{
    			a31 = -1;
    		}
    		else {
        		a31 = -list[2].poll();
			}
    		if(list[1].isEmpty())
    		{
    			a21 = -1;
    		}
    		else {
        		a21 = -list[1].poll();
			}
    		if(list[1].isEmpty())
    		{
    			a22 = -1;
    		}
    		else {
        		a22 = -list[1].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a11 = -1;
    		}
    		else {
        		a11 = -list[0].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a12 = -1;
    		}
    		else {
        		a12 = -list[0].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a13 = -1;
    		}
    		else {
        		a13 = -list[0].poll();
			}
    		max[0] = Math.max(a31, Math.max(a21, a11));
    		if(a21 > -1 && a11 > -1 && a12 > -1)
    		{
    			max[1] = Math.max(a11 + a12, a11 + a21);
    		}
    		else if(a21 > -1 && a11 > -1)
    		{
    			max[1] =a21 + a11;
    		}
    		else if(a11 > -1 && a12 > -1)
    		{
    			max[1] = a11 + a12;
    		}
    		else
    		{
    			max[1] = -1;
    		}
    		if(a11 > -1 && a12 > -1 && a13 > -1)
    		{
    			max[2] = a11 + a12 + a13;
    		}
    		else {
				max[2] = -1;
			}
    		for(int u = 0; u < 10; u++)
    		{
    			if(i == 0)
    			{
    				if(u < 3)
    				{
    					dp[i][u] = max[u];
    				}
    				else {
						dp[i][u]= -1;
					}
    			}
    			else {
    				if(u < 3)
    				{
    					dp[i][u] = Math.max(dp[i-1][u], max[u]);
    				}
    				else {
        				dp[i][u] = dp[i-1][u];
					}
					int pos1 = (u + 9)%10;
					int pos2 = (u + 8)%10;
					int pos3 = (u + 7)%10;
					if(dp[i-1][pos1] != -1 && max[0] != -1)
					{
						if(pos1 > 5 && pos1 < 9 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos1] + max[0] + max[0]);
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos1] + max[0]);
						}
					}
					if(dp[i-1][pos2] != -1 && max[1] != -1)
					{
						if(pos2 > 5 && pos2 < 9 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos2] + max[1] + Math.max(a11, a21));
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos2] + max[1]);
						}
					}
					if(dp[i-1][pos3] != -1 && max[2] != -1)
					{
						if(pos2 > 5 && pos3 < 9 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos3] + max[2] + a11);
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos3] + max[2]);
						}
					}
				}
    		}
    	}
    	long res = 0;
    	for(int u = 0; u < 10; u++)
		{
    		if(res < dp[n-1][u])
    		{
    			res = dp[n-1][u];
    		}
		}
    	System.out.println(res);
    }
}


import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner stdin = new Scanner(System.in);
        /*int n = stdin.nextInt();
        for(int i = 0; i < n; i++)
        {
        	test(stdin);
        }*/
        test(stdin);
        stdin.close();
    }
    public static void test(Scanner stdin)
    {
    	int n = stdin.nextInt();
    	long[][] dp = new long[n][10];
    	long max[] = new long[3];
    	for(int i = 0; i< n; i++)
    	{
    		int m = stdin.nextInt();
    		PriorityQueue<Integer>[] list = new PriorityQueue[3];
    		for(int u = 0; u < 3; u++)
    		{
    			list[u] = new PriorityQueue();
    		}
    		for(int u = 0; u < m; u++)
    		{
    			list[stdin.nextInt() - 1].add(-stdin.nextInt());
    		}
    		long a31,a21,a22,a11,a12,a13;
    		if(list[2].isEmpty())
    		{
    			a31 = -1;
    		}
    		else {
        		a31 = -list[2].poll();
			}
    		if(list[1].isEmpty())
    		{
    			a21 = -1;
    		}
    		else {
        		a21 = -list[1].poll();
			}
    		if(list[1].isEmpty())
    		{
    			a22 = -1;
    		}
    		else {
        		a22 = -list[1].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a11 = -1;
    		}
    		else {
        		a11 = -list[0].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a12 = -1;
    		}
    		else {
        		a12 = -list[0].poll();
			}
    		if(list[0].isEmpty())
    		{
    			a13 = -1;
    		}
    		else {
        		a13 = -list[0].poll();
			}
    		max[0] = Math.max(a31, Math.max(a21, a11));
    		if(a21 > -1 && a11 > -1 && a12 > -1)
    		{
    			max[1] = Math.max(a11 + a12, a11 + a21);
    		}
    		else if(a21 > -1 && a11 > -1)
    		{
    			max[1] =a21 + a11;
    		}
    		else if(a11 > -1 && a12 > -1)
    		{
    			max[1] = a11 + a12;
    		}
    		else
    		{
    			max[1] = -1;
    		}
    		if(a11 > -1 && a12 > -1 && a13 > -1)
    		{
    			max[2] = a11 + a12 + a13;
    		}
    		else {
				max[2] = -1;
			}
    		for(int u = 0; u < 10; u++)
    		{
    			if(i == 0)
    			{
    				if(u < 3)
    				{
    					dp[i][u] = max[u];
    				}
    				else {
						dp[i][u]= -1;
					}
    			}
    			else {
    				if(u < 3)
    				{
    					dp[i][u] = Math.max(dp[i-1][u], max[u]);
    				}
    				else {
        				dp[i][u] = dp[i-1][u];
					}
					int pos1 = (u + 9)%10;
					int pos2 = (u + 8)%10;
					int pos3 = (u + 7)%10;
					if(dp[i-1][pos1] != -1 && max[0] != -1)
					{
						if(pos1 > 5 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos1] + max[0] + max[0]);
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos1] + max[0]);
						}
					}
					if(dp[i-1][pos2] != -1 && max[1] != -1)
					{
						if(pos1 > 5 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos2] + max[1] + Math.max(a11, a21));
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos2] + max[1]);
						}
					}
					if(dp[i-1][pos3] != -1 && max[2] != -1)
					{
						if(pos1 > 5 && (u == 9 || u < 2))
						{
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos3] + max[2] + a11);
						}
						else {
							dp[i][u] = Math.max(dp[i][u], dp[i-1][pos3] + max[2]);
						}
					}
				}
    		}
    	}
    	long res = 0;
    	for(int u = 0; u < 10; u++)
		{
    		if(res < dp[n-1][u])
    		{
    			res = dp[n-1][u];
    		}
		}
    	System.out.println(res);
    }
}


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class Main {

	static class Task {

		int NN = 200005;
		int MOD = 1000000007;
		int INF = 2000000000;
		long INFINITY = 2000000000000000000L;

		class Triplet{
			Long cost, damage;
			Integer  turn;

			public Triplet(int turn, Long cost, Long damage) {
				super();
				this.turn = turn;
				this.cost = cost;
				this.damage = damage;
			}

			@Override
			public String toString() {
				return "Triplet [cost=" + cost + ", damage=" + damage + ", turn=" + turn + "]";
			}

		}

		List<Triplet> a;

		long [][][] dp;

		long rec(int i, int cost, int ten) {
			if(i == a.size())
				return 0;
			if(dp[i][cost][ten] != -1)
				return dp[i][cost][ten];
			int newCost = cost;
			if(i+1 < a.size() && a.get(i + 1).turn != a.get(i).turn)
				newCost = 0;
			long ret = rec(i + 1, newCost, ten);
			if(cost + a.get(i).cost <= 3) {
				long damage = a.get(i).damage;
				int newTen = (ten + 1);
				if(newTen == 10) {
					newTen -= 10;
					damage = 2L* damage;
				}
				newCost = cost + a.get(i).cost.intValue();
				if(i+1 < a.size() && a.get(i + 1).turn != a.get(i).turn)
					newCost = 0;
				ret = Math.max(ret,
						damage + rec(i + 1, newCost, newTen));
			}
			return dp[i][cost][ten] = ret;
		}

		public void solve(InputReader in, PrintWriter out) {
			int n = in.nextInt();
			a = new ArrayList<>();
			int turn = 0;
			for(int i=1;i<=n;++i) {
				int k = in.nextInt();++turn;
				while(k-->0) {
					a.add(new Triplet(turn, in.nextLong(), in.nextLong()));
				}
			}
			dp = new long[a.size()][4][10];
			for(int i=0;i<a.size();++i) {
				for(int ii=0;ii<4;++ii) {
					for(int iii=0;iii<10;++iii) {
						dp[i][ii][iii] = -1;
					}
				}
			}
			out.println(rec(0, 0, 0));
		}

	}

	static void prepareIO(boolean isFileIO) {
		//long t1 = System.currentTimeMillis();
		Task solver = new Task();
		// Standard IO
		if(!isFileIO) {
			InputStream inputStream = System.in;
	        OutputStream outputStream = System.out;
	        InputReader in = new InputReader(inputStream);
	        PrintWriter out = new PrintWriter(outputStream);
	        solver.solve(in, out);
	        //out.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        out.close();
		}
        // File IO
		else {
			String IPfilePath = System.getProperty("user.home") + "/Downloads/ip.in";
	        String OPfilePath = System.getProperty("user.home") + "/Downloads/op.out";
	        InputReader fin = new InputReader(IPfilePath);
	        PrintWriter fout = null;
	        try {
				fout = new PrintWriter(new File(OPfilePath));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
	        solver.solve(fin, fout);
	        //fout.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        fout.close();
		}
	}

	public static void main(String[] args) {
        prepareIO(false);
	}

	static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public InputReader(String filePath) {
        	File file = new File(filePath);
            try {
				reader = new BufferedReader(new FileReader(file));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            tokenizer = null;
        }

        public String nextLine() {
        	String str = "";
        	try {
				str = reader.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	return str;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
        	return Double.parseDouble(next());
        }

    }

}


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class Main {

	static class Task {

		int NN = 200005;
		int MOD = 1000000007;
		int INF = 2000000000;
		long INFINITY = 2000000000000000000L;

		long [][] dp;

		long rec(int i, int ten) {
			if(i == n)
				return 0;
			if(dp[i][ten] != -1)
				return dp[i][ten];
			long ret = rec(i + 1, ten);
			for(int j=0;j<6;++j) {
				if(D[i][j][0] == -1)
					continue;
				int newTen = ten + (int)D[i][j][0];
				long damage = D[i][j][1];
				if(newTen >= 10) {
					newTen -= 10;
					damage += D[i][j][2];
				}
				ret = Math.max(ret, damage + rec(i + 1, newTen));
			}
			return dp[i][ten] = ret;
		}

		//size totatdamage maxdamage
		long [][][]D;
		int n;

		public void solve(InputReader in, PrintWriter out) {
			n = in.nextInt();
			D = new long[n][6][3];
			for(int i=0;i<n;++i) {
				for(int j=0;j<6;++j) {
					for(int k=0;k<3;++k)
						D[i][j][k] = -1;
				}
			}
			for(int ii=1;ii<=n;++ii) {
				int k = in.nextInt();
				List<Long> one = new ArrayList<>();
				List<Long> two = new ArrayList<>();
				List<Long> three = new ArrayList<>();
				while(k-->0) {
					long cost = in.nextLong();
					long damage = in.nextLong();
					if(cost == 1) {
						one.add(damage);
					} else if(cost == 2) {
						two.add(damage);
					} else {
						three.add(damage);
					}
				}
				Collections.sort(one, (i, j) -> j.compareTo(i));
				Collections.sort(two, (i, j) -> j.compareTo(i));
				Collections.sort(three, (i, j) -> j.compareTo(i));
				if(one.size() >= 1) {
					D[ii - 1][0][0] = 1;
					D[ii - 1][0][1] = one.get(0);
					D[ii - 1][0][2] = one.get(0);
				}
				if(one.size() >= 2) {
					D[ii - 1][1][0] = 2;
					D[ii - 1][1][1] = one.get(0) + one.get(1);
					D[ii - 1][1][2] = one.get(0);
				}
				if(one.size() >= 3) {
					D[ii - 1][2][0] = 3;
					D[ii - 1][2][1] = one.get(0) + one.get(1) + one.get(2);
					D[ii - 1][2][2] = one.get(0);
				}
				if(two.size() >= 1) {
					D[ii - 1][3][0] = 1;
					D[ii - 1][3][1] = two.get(0);
					D[ii - 1][3][2] = two.get(0);
				}
				if(three.size() >= 1) {
					D[ii - 1][4][0] = 1;
					D[ii - 1][4][1] = three.get(0);
					D[ii - 1][4][2] = three.get(0);
				}
				if(two.size() >= 1 && one.size() >= 1) {
					D[ii - 1][5][0] = 2;
					D[ii - 1][5][1] = two.get(0) + one.get(0);
					D[ii - 1][5][2] = Math.max(one.get(0), two.get(0));
				}
			}
			dp = new long[n][10];
			for(int i=0;i<n;++i) {
				for(int iii=0;iii<10;++iii) {
					dp[i][iii] = -1;
				}
			}
			out.println(rec(0, 0));
		}

	}

	static void prepareIO(boolean isFileIO) {
		//long t1 = System.currentTimeMillis();
		Task solver = new Task();
		// Standard IO
		if(!isFileIO) {
			InputStream inputStream = System.in;
	        OutputStream outputStream = System.out;
	        InputReader in = new InputReader(inputStream);
	        PrintWriter out = new PrintWriter(outputStream);
	        solver.solve(in, out);
	        //out.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        out.close();
		}
        // File IO
		else {
			String IPfilePath = System.getProperty("user.home") + "/Downloads/ip.in";
	        String OPfilePath = System.getProperty("user.home") + "/Downloads/op.out";
	        InputReader fin = new InputReader(IPfilePath);
	        PrintWriter fout = null;
	        try {
				fout = new PrintWriter(new File(OPfilePath));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
	        solver.solve(fin, fout);
	        //fout.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        fout.close();
		}
	}

	public static void main(String[] args) {
        prepareIO(false);
	}

	static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public InputReader(String filePath) {
        	File file = new File(filePath);
            try {
				reader = new BufferedReader(new FileReader(file));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            tokenizer = null;
        }

        public String nextLine() {
        	String str = "";
        	try {
				str = reader.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	return str;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
        	return Double.parseDouble(next());
        }

    }

}


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class Main {

	static class Task {

		int NN = 200005;
		int MOD = 1000000007;
		int INF = 2000000000;
		long INFINITY = 2000000000000000000L;

		class Triplet {
			Long cost, damage;
			Integer  turn;

			public Triplet(int turn, Long cost, Long damage) {
				super();
				this.turn = turn;
				this.cost = cost;
				this.damage = damage;
			}

			@Override
			public String toString() {
				return "Triplet [cost=" + cost + ", damage=" + damage + ", turn=" + turn + "]";
			}

		}

		List<Triplet> a;

		long [][] dp;

		long rec(int i, int ten) {
			if(i == n)
				return 0;
			if(dp[i][ten] != -1)
				return dp[i][ten];
			long ret = rec(i + 1, ten);
			for(int j=0;j<6;++j) {
				if(D[i][j][0] == -1)
					continue;
				int newTen = ten + (int)D[i][j][0];
				long damage = D[i][j][1];
				if(newTen >= 10) {
					newTen -= 10;
					damage += D[i][j][2];
				}
				ret = Math.max(ret, damage + rec(i + 1, newTen));
			}
			return dp[i][ten] = ret;
		}

		//size totatdamage maxdamage
		long [][][]D;
		int n;

		public void solve(InputReader in, PrintWriter out) {
			n = in.nextInt();
			a = new ArrayList<>();
			int turn = 0;
			for(int i=1;i<=n;++i) {
				int k = in.nextInt();++turn;
				while(k-->0) {
					a.add(new Triplet(turn, in.nextLong(), in.nextLong()));
				}
			}
			D = new long[n][6][3];
			for(int i=0;i<n;++i) {
				for(int j=0;j<6;++j) {
					for(int k=0;k<3;++k)
						D[i][j][k] = -1;
				}
			}
			List<Triplet> one = new ArrayList<>();
			List<Triplet> two = new ArrayList<>();
			List<Triplet> three = new ArrayList<>();
			Triplet pt = null;
			int ptr = -1;
			for(Triplet t: a) {
				if(pt != null && pt.turn != t.turn) {
					++ptr;
					Collections.sort(one, (i, j) -> j.damage.compareTo(i.damage));
					Collections.sort(two, (i, j) -> j.damage.compareTo(i.damage));
					Collections.sort(three, (i, j) -> j.damage.compareTo(i.damage));
					if(one.size() >= 1) {
						D[ptr][0][0] = 1;
						D[ptr][0][1] = one.get(0).damage;
						D[ptr][0][2] = one.get(0).damage;
					}
					if(one.size() >= 2) {
						D[ptr][1][0] = 2;
						D[ptr][1][1] = one.get(0).damage + one.get(1).damage;
						D[ptr][1][2] = one.get(0).damage;
					}
					if(one.size() >= 3) {
						D[ptr][2][0] = 3;
						D[ptr][2][1] = one.get(0).damage + one.get(1).damage + one.get(2).damage;
						D[ptr][2][2] = one.get(0).damage;
					}
					if(two.size() >= 1) {
						D[ptr][3][0] = 1;
						D[ptr][3][1] = two.get(0).damage;
						D[ptr][3][2] = two.get(0).damage;
					}
					if(three.size() >= 1) {
						D[ptr][4][0] = 1;
						D[ptr][4][1] = three.get(0).damage;
						D[ptr][4][2] = three.get(0).damage;
					}
					if(two.size() >= 1 && one.size() >= 1) {
						D[ptr][5][0] = 2;
						D[ptr][5][1] = two.get(0).damage + one.get(0).damage;
						D[ptr][5][2] = Math.max(one.get(0).damage, two.get(0).damage);
					}
					one = new ArrayList<>();
					two = new ArrayList<>();
					three = new ArrayList<>();
				}
				if(t.cost == 1) {
					one.add(t);
				} else if(t.cost == 2) {
					two.add(t);
				} else {
					three.add(t);
				}
				pt = t;
			}
			++ptr;
			Collections.sort(one, (i, j) -> j.damage.compareTo(i.damage));
			Collections.sort(two, (i, j) -> j.damage.compareTo(i.damage));
			Collections.sort(three, (i, j) -> j.damage.compareTo(i.damage));
			if(one.size() >= 1) {
				D[ptr][0][0] = 1;
				D[ptr][0][1] = one.get(0).damage;
				D[ptr][0][2] = one.get(0).damage;
			}
			if(one.size() >= 2) {
				D[ptr][1][0] = 2;
				D[ptr][1][1] = one.get(0).damage + one.get(1).damage;
				D[ptr][1][2] = one.get(0).damage;
			}
			if(one.size() >= 3) {
				D[ptr][2][0] = 3;
				D[ptr][2][1] = one.get(0).damage + one.get(1).damage + one.get(2).damage;
				D[ptr][2][2] = one.get(0).damage;
			}
			if(two.size() >= 1) {
				D[ptr][3][0] = 1;
				D[ptr][3][1] = two.get(0).damage;
				D[ptr][3][2] = two.get(0).damage;
			}
			if(three.size() >= 1) {
				D[ptr][4][0] = 1;
				D[ptr][4][1] = three.get(0).damage;
				D[ptr][4][2] = three.get(0).damage;
			}
			if(two.size() >= 1 && one.size() >= 1) {
				D[ptr][5][0] = 2;
				D[ptr][5][1] = two.get(0).damage + one.get(0).damage;
				D[ptr][5][2] = Math.max(one.get(0).damage, two.get(0).damage);
			}
			dp = new long[n][10];
			for(int i=0;i<n;++i) {
				for(int iii=0;iii<10;++iii) {
					dp[i][iii] = -1;
				}
			}
			out.println(rec(0, 0));
		}

	}

	static void prepareIO(boolean isFileIO) {
		//long t1 = System.currentTimeMillis();
		Task solver = new Task();
		// Standard IO
		if(!isFileIO) {
			InputStream inputStream = System.in;
	        OutputStream outputStream = System.out;
	        InputReader in = new InputReader(inputStream);
	        PrintWriter out = new PrintWriter(outputStream);
	        solver.solve(in, out);
	        //out.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        out.close();
		}
        // File IO
		else {
			String IPfilePath = System.getProperty("user.home") + "/Downloads/ip.in";
	        String OPfilePath = System.getProperty("user.home") + "/Downloads/op.out";
	        InputReader fin = new InputReader(IPfilePath);
	        PrintWriter fout = null;
	        try {
				fout = new PrintWriter(new File(OPfilePath));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
	        solver.solve(fin, fout);
	        //fout.println("time(s): " + (1.0*(System.currentTimeMillis()-t1))/1000.0);
	        fout.close();
		}
	}

	public static void main(String[] args) {
        prepareIO(false);
	}

	static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public InputReader(String filePath) {
        	File file = new File(filePath);
            try {
				reader = new BufferedReader(new FileReader(file));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
            tokenizer = null;
        }

        public String nextLine() {
        	String str = "";
        	try {
				str = reader.readLine();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	return str;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public double nextDouble() {
        	return Double.parseDouble(next());
        }

    }

}


import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    static final int UNCALC = -1;
    static long[][] memo;
    static Pair[][] adjList;
    static int n;

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        n = sc.nextInt();
        adjList = new Pair[n][];
        for (int i = 0; i < n; i++) {
            int k = sc.nextInt();
            adjList[i] = new Pair[k];
            for (int j = 0; j < k; j++)
                adjList[i][j] = new Pair(sc.nextInt(), sc.nextInt());
            Arrays.sort(adjList[i]);
        }
        memo = new long[11][n];
        for (long[] a : memo)
            Arrays.fill(a, UNCALC);
        out.println(dp(0, 0));
        out.flush();
        out.close();
    }

    static long dp(int p, int turn) {
        if (turn == n)
            return 0;
        if (memo[p][turn] != UNCALC)
            return memo[p][turn];
        long best = dp(p, turn + 1);
        int three = 0;
        int two = 0;
        int[] ones = new int[3];
        int c = 0;
        for (Pair pp : adjList[turn]) {
            if (pp.c == 3 && three == 0) three = pp.d;
            else if (pp.c == 2 && two == 0) two = pp.d;
            else if (pp.c == 1 && c != 3) ones[c++] = pp.d;
        }
        //play one card only
        best = Math.max(best, Math.max(three, Math.max(two, ones[0])) * (p == 10 ? 2 : 1) + dp((p + 1) % 11, turn + 1));
        //Play two cards
        best = Math.max(best, Math.max(two, ones[0]) * (p == 9 ? 2 : 1) + Math.min(two, ones[0]) + dp((p + 2) % 11, turn + 1));
        best = Math.max(best, ones[0] * (p == 9 ? 2 : 1) + ones[1] + dp((p + 2) % 11, turn + 1));
        //play three cards
        best = Math.max(best, ones[0] * (p == 8 ? 2 : 1) + ones[1] + ones[2] + dp((p + 3) % 11, turn + 1));

        return memo[p][turn] = best;
    }

    static class Pair implements Comparable<Pair> {
        int c, d;

        public Pair(int c, int d) {
            this.c = c;
            this.d = d;
        }

        @Override
        public int compareTo(Pair o) {
            return o.d - d;
        }
    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {
            br = new BufferedReader(new InputStreamReader(system));
        }

        public String next() throws IOException {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

        public String nextLine() throws IOException {
            return br.readLine();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }

        public char nextChar() throws IOException {
            return next().charAt(0);
        }

        public Long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public boolean ready() throws IOException {
            return br.ready();
        }

        public int[] nextIntArray(int n) throws IOException {
            int[] a = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public long[] nextLongArray(int n) throws IOException {
            long[] a = new long[n];
            for (int i = 0; i < n; i++)
                a[i] = nextLong();
            return a;
        }

        public Integer[] nextIntegerArray(int n) throws IOException {
            Integer[] a = new Integer[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public double[] nextDoubleArray(int n) throws IOException {
            double[] ans = new double[n];
            for (int i = 0; i < n; i++)
                ans[i] = nextDouble();
            return ans;
        }

        public short nextShort() throws IOException {
            return Short.parseShort(next());
        }

    }

}


import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {
    static final int UNCALC = -1;
    static long[][] memo;
    static Turn[] turns;
    static int n;

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        n = sc.nextInt();
        turns = new Turn[n];
        for (int i = 0; i < n; i++) {
            int k = sc.nextInt();
            turns[i] = new Turn();
            while (k-- > 0) {
                int c = sc.nextInt();
                if (c == 1) turns[i].addOne(sc.nextInt());
                else if (c == 2) turns[i].addTwo(sc.nextInt());
                else turns[i].addThree(sc.nextInt());
            }
            turns[i].sortOnes();
        }
        memo = new long[10][n];
        for (long[] a : memo)
            Arrays.fill(a, UNCALC);
        out.println(dp(0, 0));
        out.flush();
        out.close();
    }

    static long dp(int p, int turn) {
        if (turn == n)
            return 0;
        p %= 10;
        if (memo[p][turn] != UNCALC)
            return memo[p][turn];
        //Play zero cards
        long best = dp(p, turn + 1);
        int three = turns[turn].three;
        int two = turns[turn].two;
        int[] ones = turns[turn].ones;
        int c = 0;
        //play one card only
        best = Math.max(best, Math.max(three, Math.max(two, ones[0])) * (p == 9 ? 2l : 1) + dp(p + 1, turn + 1));
        //Play two cards
        if (two != 0 && ones[0] != 0)
            best = Math.max(best, Math.max(two, ones[0]) * (p >= 8 ? 2l : 1) + Math.min(two, ones[0]) + dp(p + 2, turn + 1));
        if (ones[1] != 0)
            best = Math.max(best, ones[0] * (p >= 8 ? 2l : 1) + ones[1] + dp(p + 2, turn + 1));
        //play three cards
        if (ones[2] != 0)
            best = Math.max(best, ones[0] * (p >= 7 ? 2l : 1) + ones[1] + ones[2] + dp(p + 3, turn + 1));
        return memo[p][turn] = best;
    }

    static class Turn {
        ArrayList<Integer> one;
        int three, two, ones[];

        public Turn() {
            one = new ArrayList<>();
            ones = new int[3];
        }

        void addThree(int x) {
            three = Math.max(three, x);
        }

        void addTwo(int x) {
            two = Math.max(two, x);
        }

        void addOne(int x) {
            one.add(x);
        }

        void sortOnes() {
            Collections.sort(one);
            int j = 0;
            for (int i = one.size() - 1; i >= 0 && j < 3; i--)
                ones[j++] = one.get(i);

        }

        @Override
        public String toString() {
            return "Turn{" +
                    "three=" + three +
                    ", two=" + two +
                    ", ones=" + Arrays.toString(ones) +
                    '}';
        }
    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {
            br = new BufferedReader(new InputStreamReader(system));
        }

        public String next() throws IOException {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

        public String nextLine() throws IOException {
            return br.readLine();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }

        public char nextChar() throws IOException {
            return next().charAt(0);
        }

        public Long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public boolean ready() throws IOException {
            return br.ready();
        }

        public int[] nextIntArray(int n) throws IOException {
            int[] a = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public long[] nextLongArray(int n) throws IOException {
            long[] a = new long[n];
            for (int i = 0; i < n; i++)
                a[i] = nextLong();
            return a;
        }

        public Integer[] nextIntegerArray(int n) throws IOException {
            Integer[] a = new Integer[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public double[] nextDoubleArray(int n) throws IOException {
            double[] ans = new double[n];
            for (int i = 0; i < n; i++)
                ans[i] = nextDouble();
            return ans;
        }

        public short nextShort() throws IOException {
            return Short.parseShort(next());
        }

    }

}


import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {
    static final int UNCALC = -1;
    static long[][] memo;
    static Turn[] turns;
    static int n;

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        n = sc.nextInt();
        turns = new Turn[n];
        for (int i = 0; i < n; i++) {
            int k = sc.nextInt();
            turns[i] = new Turn();
            while (k-- > 0) {
                int c = sc.nextInt();
                if (c == 1) turns[i].addOne(sc.nextInt());
                else if (c == 2) turns[i].addTwo(sc.nextInt());
                else turns[i].addThree(sc.nextInt());
            }
            turns[i].sortOnes();
        }
        memo = new long[10][n];
        for (long[] a : memo)
            Arrays.fill(a, UNCALC);
        out.println(dp(0, 0));
        out.flush();
        out.close();
    }

    static long dp(int p, int turn) {
        if (turn == n)
            return 0;
        p %= 10;
        if (memo[p][turn] != UNCALC)
            return memo[p][turn];
        //Play zero cards
        long best = dp(p, turn + 1);
        int three = turns[turn].three;
        int two = turns[turn].two;
        int[] ones = turns[turn].ones;
        int c = 0;
        //play one card only
        best = Math.max(best, Math.max(three, Math.max(two, ones[0])) * (p == 9 ? 2 : 1) + dp(p + 1, turn + 1));
        //Play two cards
        best = Math.max(best, Math.max(two, ones[0]) * (p >= 8 ? 2 : 1) + Math.min(two, ones[0]) + dp(p + 2, turn + 1));
        best = Math.max(best, ones[0] * (p >= 8 ? 2 : 1) + ones[1] + dp(p + 2, turn + 1));
        //play three cards
        best = Math.max(best, ones[0] * (p >= 7 ? 2 : 1) + ones[1] + ones[2] + dp(p + 3, turn + 1));
        return memo[p][turn] = best;
    }

    static class Turn {
        ArrayList<Integer> one;
        int three, two, ones[];

        public Turn() {
            one = new ArrayList<>();
            ones = new int[3];
        }

        void addThree(int x) {
            three = Math.max(three, x);
        }

        void addTwo(int x) {
            two = Math.max(two, three);
        }

        void addOne(int x) {
            one.add(x);
        }

        void sortOnes() {
            Collections.sort(one);
            int j = 0;
            for (int i = one.size() - 1; i >= 0 && j < 3; i--)
                ones[j++] = one.get(i);

        }
    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {
            br = new BufferedReader(new InputStreamReader(system));
        }

        public String next() throws IOException {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

        public String nextLine() throws IOException {
            return br.readLine();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }

        public char nextChar() throws IOException {
            return next().charAt(0);
        }

        public Long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public boolean ready() throws IOException {
            return br.ready();
        }

        public int[] nextIntArray(int n) throws IOException {
            int[] a = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public long[] nextLongArray(int n) throws IOException {
            long[] a = new long[n];
            for (int i = 0; i < n; i++)
                a[i] = nextLong();
            return a;
        }

        public Integer[] nextIntegerArray(int n) throws IOException {
            Integer[] a = new Integer[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public double[] nextDoubleArray(int n) throws IOException {
            double[] ans = new double[n];
            for (int i = 0; i < n; i++)
                ans[i] = nextDouble();
            return ans;
        }

        public short nextShort() throws IOException {
            return Short.parseShort(next());
        }

    }

}


import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
    static final int UNCALC = -1;
    static long[][] memo;
    static Pair[][] adjList;
    static int n;

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        n = sc.nextInt();
        adjList = new Pair[n][];
        for (int i = 0; i < n; i++) {
            int k = sc.nextInt();
            adjList[i] = new Pair[k];
            for (int j = 0; j < k; j++)
                adjList[i][j] = new Pair(sc.nextInt(), sc.nextInt());
            Arrays.sort(adjList[i]);
        }
        memo = new long[11][n];
        for (long[] a : memo)
            Arrays.fill(a, UNCALC);
        out.println(dp(0, 0));
        out.flush();
        out.close();
    }

    static long dp(int p, int turn) {
        if (turn == n)
            return 0;
        if (memo[p][turn] != UNCALC)
            return memo[p][turn];
        long best = dp(p, turn + 1);
        int three = 0;
        int two = 0;
        int[] ones = new int[3];
        int c = 0;
        for (Pair pp : adjList[turn]) {
            if (pp.c == 3 && three == 0) three = pp.d;
            else if (pp.c == 2 && two == 0) two = pp.d;
            else if (pp.c == 1 && c != 3) ones[c++] = pp.d;
        }
        if (p == 10) {
            best = Math.max(best, three * 2 + dp(0, turn + 1));
            best = Math.max(best, two * 2 + dp(0, turn + 1));
            best = Math.max(best, ones[0] * 2 + dp(0, turn + 1));
        } else if (p == 9) {
            best = Math.max(best, Math.max(two, ones[0]) * 2 + Math.min(two, ones[1]) + dp(0, turn + 1));
            best = Math.max(best, ones[0] * 2 + ones[1] + dp(0, turn + 1));
        } else if (p == 8) {
            best = Math.max(best, ones[0] * 2 + ones[1] + ones[2] + dp(0, turn + 1));
        } else {
            best = Math.max(best, three + dp(p + 1, turn + 1));
            best = Math.max(best, two + dp(p + 1, turn + 1));
            best = Math.max(best, ones[0] + dp(p + 1, turn + 1));
            best = Math.max(best, two + ones[0] + dp(p + 2, turn + 1));
            best = Math.max(best, ones[0] + ones[1] + dp(p + 2, turn + 1));
            best = Math.max(best, ones[0] + ones[1] + ones[2] + dp(p + 3, turn + 1));

        }
        return memo[p][turn] = best;
    }

    static class Pair implements Comparable<Pair> {
        int c, d;

        public Pair(int c, int d) {
            this.c = c;
            this.d = d;
        }

        @Override
        public int compareTo(Pair o) {
            return o.d - d;
        }
    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {
            br = new BufferedReader(new InputStreamReader(system));
        }

        public String next() throws IOException {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

        public String nextLine() throws IOException {
            return br.readLine();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }

        public char nextChar() throws IOException {
            return next().charAt(0);
        }

        public Long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public boolean ready() throws IOException {
            return br.ready();
        }

        public int[] nextIntArray(int n) throws IOException {
            int[] a = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public long[] nextLongArray(int n) throws IOException {
            long[] a = new long[n];
            for (int i = 0; i < n; i++)
                a[i] = nextLong();
            return a;
        }

        public Integer[] nextIntegerArray(int n) throws IOException {
            Integer[] a = new Integer[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public double[] nextDoubleArray(int n) throws IOException {
            double[] ans = new double[n];
            for (int i = 0; i < n; i++)
                ans[i] = nextDouble();
            return ans;
        }

        public short nextShort() throws IOException {
            return Short.parseShort(next());
        }

    }

}


import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringTokenizer;

public class Main {
    static final int UNCALC = -1;
    static long[][] memo;
    static Turn[] turns;
    static int n;

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        PrintWriter out = new PrintWriter(System.out);
        n = sc.nextInt();
        turns = new Turn[n];
        for (int i = 0; i < n; i++) {
            int k = sc.nextInt();
            turns[i] = new Turn();
            while (k-- > 0) {
                int c = sc.nextInt();
                if (c == 1) turns[i].addOne(sc.nextInt());
                else if (c == 2) turns[i].addTwo(sc.nextInt());
                else turns[i].addThree(sc.nextInt());
            }
            turns[i].sortOnes();
        }
        memo = new long[10][n];
        for (long[] a : memo)
            Arrays.fill(a, UNCALC);
        out.println(dp(0, 0));
        out.flush();
        out.close();
    }

    static long dp(int p, int turn) {
        if (turn == n)
            return 0;
        p %= 10;
        if (memo[p][turn] != UNCALC)
            return memo[p][turn];
        //Play zero cards
        long best = dp(p, turn + 1);
        int three = turns[turn].three;
        int two = turns[turn].two;
        int[] ones = turns[turn].ones;
        int c = 0;
        //play one card only
        best = Math.max(best, Math.max(three, Math.max(two, ones[0])) * (p == 9 ? 2 : 1) + dp(p + 1, turn + 1));
        //Play two cards
        best = Math.max(best, Math.max(two, ones[0]) * (p >= 8 ? 2 : 1) + Math.min(two, ones[0]) + dp(p + 2, turn + 1));
        best = Math.max(best, ones[0] * (p >= 8 ? 2 : 1) + ones[1] + dp(p + 2, turn + 1));
        //play three cards
        best = Math.max(best, ones[0] * (p >= 7 ? 2 : 1) + ones[1] + ones[2] + dp(p + 3, turn + 1));
        return memo[p][turn] = best;
    }

    static class Turn {
        ArrayList<Integer> one;
        int three, two, ones[];

        public Turn() {
            one = new ArrayList<>();
            ones = new int[3];
        }

        void addThree(int x) {
            three = Math.max(three, x);
        }

        void addTwo(int x) {
            two = Math.max(two, x);
        }

        void addOne(int x) {
            one.add(x);
        }

        void sortOnes() {
            Collections.sort(one);
            int j = 0;
            for (int i = one.size() - 1; i >= 0 && j < 3; i--)
                ones[j++] = one.get(i);

        }

        @Override
        public String toString() {
            return "Turn{" +
                    "three=" + three +
                    ", two=" + two +
                    ", ones=" + Arrays.toString(ones) +
                    '}';
        }
    }

    static class Scanner {
        StringTokenizer st;
        BufferedReader br;

        public Scanner(InputStream system) {
            br = new BufferedReader(new InputStreamReader(system));
        }

        public String next() throws IOException {
            while (st == null || !st.hasMoreTokens()) st = new StringTokenizer(br.readLine());
            return st.nextToken();
        }

        public String nextLine() throws IOException {
            return br.readLine();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }

        public char nextChar() throws IOException {
            return next().charAt(0);
        }

        public Long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public boolean ready() throws IOException {
            return br.ready();
        }

        public int[] nextIntArray(int n) throws IOException {
            int[] a = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public long[] nextLongArray(int n) throws IOException {
            long[] a = new long[n];
            for (int i = 0; i < n; i++)
                a[i] = nextLong();
            return a;
        }

        public Integer[] nextIntegerArray(int n) throws IOException {
            Integer[] a = new Integer[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public double[] nextDoubleArray(int n) throws IOException {
            double[] ans = new double[n];
            for (int i = 0; i < n; i++)
                ans[i] = nextDouble();
            return ans;
        }

        public short nextShort() throws IOException {
            return Short.parseShort(next());
        }

    }

}