Your method of passing to the script is valid. You could rewrite your script as
#!/bin/bash
arg1=$1
arg2=$2
for i in $(seq 3 $#); do
#reference each thing in arr1 with ${!i}
done
Though that's admittedly hard to read. {!var} is indirection (basically, you ask what for what i is equal to, and then you ask for whatever that is equal to, so if i=1, you're doing ${!i} is the same as $1).
This doesn't keep the array together, though. If you needed arr1 to stay arr1 in the script, you could do:
#!/bin/bash
arg1=$1
shift
arg2=$1
shift
arr1=("$@")
Bash takes command-line arguments by their position relative to the script name (which gets the variable $0). For your scenario, arg1 is $1, arg2 is $2, and arr1 is $3 $4 $5 ..., depending on the length of your array. shift will move all the arguments down 1, making 1=$2, 2=$3, etc. for all the arguments you have. (It doesn't change $0 from the script name; that stays the same.)
$@ grabs ALL the command-line variables, starting at $1. However, because we shifted away the variables for arg1 and arg2, grabbing all the variables really means that we're only grabbing the variables that were originally part of our array.
If you have multiple arrays, or the array is first in the list, then you have to know how many elements are in the array for it to work. Let's say you wanted to do test.sh arr1 var1 arr2, and you knew both arrays were length 3:
#!/bin/bash
arr1=("${@:1:4}")
shift ${#arr1[@]} #shift by the length of arr1
arg1=$1
shift
arr2=("$@")
Hopefully you can see how this could work with a variable number of elements in the array, by passing the length of the array first, grabbing that with size=$(( 1 + $1 )), then shifting and grabbing the array with ${@:1:$size} and then doing ((size--)) and shift $size to start again with the next array size and array.