Deep down, in the hardware, all of it is stored in RAM (memory, which can be read and written). RAM is organized as separate bytes, each consisting of exactly 8 bits. Bit is zero 0 or one 1. And that is all there. No other types. Program is in machine code and the processor manipulate the RAM without understanding what value means what, it just execute commands.
There are layers of abstraction over it.
There are system routines, as digitalWrite and pinMode written in C++, there are objects like Serial, which have methods like read and parseInt (which call other routins/functions inside) and there is you code, which calls the routines and uses the objects etc. etc.
The C++ enable easier manipulation with the RAM bytes as it allows to name them (and call that variables) and manipulate them acordingly types assigned to the variables - using one byte for character type, two bytes for int type, 4 bytes for long etc. etc.
So if you increase integer, you do not need to think about increasing the lower byte and carry eventual overflow into higher byte - C++ assumes from variable declaration, that it is, what you want to do.
So back to your code.
The Serial.begin(115200); did many things, one of them was initialising serial HW part of the MCU (processor).
When some serial data came over wires to the right pin, this HW converted them into bytes and the system stored them in RAM in form of First In First Out buffer.
When you called Serial.read(); it took first byte from the buffer and returned it. You created variable char id and let the returned value be stored there. (So to memory allocated for the variable id was stored byte 65 (decimal) = 41 (hex) = 01000001 (bin) - processor does not care, there are 8 bits in one byte - decimal/hexa/bin is as you see it, not as processor see it).
Later you compared id with LED_ids[i] and as you marked both of them as chars (the later by little more complicated way via array) the C++ compiled it to compare value of those bytes and depending on result to set LED state, or not.
LED_ids is declared as array of characters with size 2 (so it takes 2 bytes in RAM) and initialised with values ‘A’ and ‘B’ so in RAM are stored bytes 65 and 66. C++ manipulates each byte separately.
When you declared variable int state the C++ reserved 2 bytes for it and know to manipulate it as 16bits long numerical (which use different manipulation, then LED_pins, which takes also 2 bytes).
You called Serial.parseInt(), which inside started reading bytes from the serial buffer - first was 32 and so was discarted as space before number, than came 49 which was used as character ‘1’ and considered valid part of integer, then came 13 which was considered carriage return and so not valid part of integer and so end of reading for this function. So far collected characters ( just ‘1’) was converted into integer value (00 01) and returned as result.
Both the bytes (00 01) was stored int variable state. (00 as high byte, 01 as low byte.)
Later it was used as second parameter for digitalWrite, so it was converted from int (2 bytes) to uint8_t (1 byte) by using just the lower byte. And later in the digitalWrite it was compared with value of LOW (which is zero 0) and action was taken acordingly.
So basically only you see it as numbers, characters, strings etc. C++ knows, what type you assigned to variables and manipulates byte acordingly your [strike]wishes[/strike] commands, MCU see only bytes and works only with bytes. (And wires around see only some voltages, not knowing, that you see it as serial communication.)
And in some other view, you program see the first byte as command “set first LED to following state”.