Don't optimize prematurely, and don't optimize when there is no benefit to doing so, except as an academic exercise. Optimization can sometimes make code harder to read; in this case you must consider if the improvement is worth the increased pain next time you need to look at the code (often it is not).
That caveat aside...
You use int for the buttonState variables. These will only ever be 1 or 0. So make it a byte (or a boolean - but a boolean takes up a whole byte anyway) instead.
Related, you can test if those variables are zero or non-zero without a comparison operator. Like, (buttonState1 >= 1 || buttonState2 >=1 || buttonState3 >=1) could be (buttonState1 || buttonState2 || buttonState3) - any non-zero value (positive or negative) in an integer datatype is true, while zero is false.
When you're feeding string literals (text enclosed in quotes with nothing special about it) to print() statements, enclose them in F() macro, ie, Serial.println("on"); becomes Serial.println(F("on")); - this puts the string into flash only, thus saving ram (otherwise, the string literals are copied into ram on initialization). The F() macro only works for Serial.print()/println() and maybe lcd.print()/println() (don't know off the top of my head - if it doesn't work, it'll give a compile error) - if you need to do more advanced manipulation with a string from flash, it's more complicated, but F() covers a lot of common use cases.
The delay() calls are blocking. If you want your sketch to react to button presses while in those delays, you have to do soemthing different. See the articles/posts here on 'doing multiple things at once' - this is covered in literally hundreds of guides and articles, very common issue.
The variables where you count up button presses could be bytes, too (but think a moment before you do this. Say some day you want to make it count to a higher number. Will 255 be high enough? It looks like that's fine here - but it's a trivial example of how you could make your life harder as a result of optimization.