Problem:
Score : 500 points

Problem Statement

You are given a string S of length N consisting of lowercase English letters.

Process Q queries of the following two types:

Type 1: change the i_q-th character of S to c_q. (Do nothing if the i_q-th character is already c_q.)

Type 2: answer the number of different characters occurring in the substring of S between the l_q-th and r_q-th characters (inclusive).

Constraints

N, Q, i_q, l_q, and r_q are integers.

S is a string consisting of lowercase English letters.

c_q is a lowercase English letter.

1 \leq N \leq 500000

1 \leq Q \leq 20000

|S| = N

1 \leq i_q \leq N

1 \leq l_q \leq r_q \leq N

There is at least one query of type 2 in each testcase.

Input

Input is given from Standard Input in the following format:

N
S
Q
Query_1
\vdots
Query_Q

Here, Query_i in the 4-th through (Q+3)-th lines is one of the following:

1 i_q c_q

2 l_q r_q

Output

For each query of type 2, print a line containing the answer.

Sample Input 1

7
abcdbbd
6
2 3 6
1 5 z
2 1 1
1 4 a
1 7 d
2 1 7

Sample Output 1

3
1
5

In the first query, cdbb contains three kinds of letters: b , c , and d, so we print 3.

In the second query, S is modified to abcdzbd.

In the third query, a contains one kind of letter: a, so we print 1.

In the fourth query, S is modified to abcazbd.

In the fifth query, S does not change and is still abcazbd.

In the sixth query, abcazbd contains five kinds of letters: a, b, c, d, and z, so we print 5.

Solution in Fortran (gfortran v4.8.4) (judge verdict: Time Limit Exceeded)

program main
  implicit none
  type arr_list
     integer :: N
     integer,allocatable :: point(:)
  end type arr_list
  type(arr_list),allocatable :: data(:)
  integer :: num(97:122)
  integer :: N, Q
  character(len=500000) :: S
  character(:),allocatable :: tmp
  character(len=1) :: c,b,c2
  integer :: a
  integer :: i,j, l,r
  integer :: out
  read(*,*) N
  read(*,*) S
  read(*,*) Q
  allocate(data(97:122))
  data(:)%N = 0
  num(:) = 0
  do i = 1,N
     data(ichar(S(i:i)))%N = data(ichar(S(i:i)))%N+1
  end do
  do i = 97,122
     if (data(i)%N > 0) allocate(data(i)%point(0:data(i)%N-1))
  end do
  do i = 1,N
     data(ichar(S(i:i)))%point( num(ichar(S(i:i))) ) = i-1
     num(ichar(S(i:i))) = num(ichar(S(i:i))) + 1
  end do
!  do i = 97,97+4
!     write(*,*) data(i)%point
!  end do
  do i = 1, Q
     read(*,'(i1,1x,a,1x,a)') a,b,c
     if (a == 1) then
        backspace(5)
        !read(*,'(i1,1x,g0,1x,a)') a,j,c
        read(*,*) a,j,c
        if (Q> 10000) then
           c2 = S(j:j)
           call insert_delete(data,j-1,c,c2)
        end if
        S(j:j) = c
     else
        backspace(5)
!        read(*,'(i1,1x,g0,1x,g0)') a,l,r
        read(*,*) a,l,r
        !tmp = S(l:r)
        !call count_char(S(l:r),out)
        if (Q> 10000) then
           call count_char(data,l-1,r-1,out)
        else
           tmp = S(l:r)
           call count_char2(tmp,out)
           deallocate(tmp)
        end if
        write(*,'(i0)') out
        !deallocate(tmp)
     end if

  end do
contains
  subroutine insert_delete(data,key,c,c2)
    implicit none
    type(arr_list) :: data(97:122)
    integer :: key ! key
    character :: c,c2 ! char
    integer,allocatable :: tmp(:)
    integer :: i,i2
    integer :: j
    integer :: n

    i = ichar(c)
    i2 = ichar(c2)
    if ( i /= i2) then ! insert and delete
       if (data(i)%N > 0) then
          j = found_point(data(i)%point(:),data(i)%n, key)
       else
          data(i)%N=data(i)%N+1
          allocate(data(i)%point(0:data(i)%N-1))
          data(i)%point(0:0) = key
          !
          ! delete
          !
          n = data(i2)%n
          data(i2)%n = data(i2)%n - 1
          if ( data(i2)%n > 0) then
             j = found_point(data(i2)%point(:),n, key)
             allocate(tmp(0:n-1))
             tmp(0:n-1) = data(i2)%point(0:n-1)
             deallocate( data(i2)%point )
             allocate( data(i2)%point(0:data(i2)%n-1) )
             data(i2)%point(0:j-1) = tmp(0:j-1)
             data(i2)%point(j:) = tmp(j+1:n-1)
             deallocate(tmp)
          else
             deallocate( data(i2)%point )
          end if
          return
       end if
       if ( j == data(i)%N) then ! out bound
          n = data(i)%n
          allocate(tmp(0:n-1))
          tmp(0:n-1) = data(i)%point(0:n-1)
          deallocate( data(i)%point )
          data(i)%n = data(i)%n + 1
          allocate( data(i)%point(0:data(i)%n-1) )
          data(i)%point(0:n-1) = tmp(0:n-1)
          data(i)%point(n) = key
          deallocate(tmp)
          !
          ! delete
          !
          n = data(i2)%n
          data(i2)%n = data(i2)%n - 1
          if ( data(i2)%n > 0) then
             j = found_point(data(i2)%point(:),n, key)
             allocate(tmp(0:n-1))
             tmp(0:n-1) = data(i2)%point(0:n-1)
             deallocate( data(i2)%point )
             allocate( data(i2)%point(0:data(i2)%n-1) )
             data(i2)%point(0:j-1) = tmp(0:j-1)
             data(i2)%point(j:) = tmp(j+1:n-1)
             deallocate(tmp)
          else
             deallocate( data(i2)%point )
          end if
          return
       else
          n = data(i)%n
          allocate(tmp(0:n-1))
          tmp(0:n-1) = data(i)%point(0:n-1)
          deallocate( data(i)%point )
          data(i)%n = data(i)%n + 1
          allocate( data(i)%point(0:data(i)%n-1) )
          if ( j == 0 ) then
             data(i)%point(0:0) = key
             data(i)%point(j+1:) = tmp(0:n-1)
          else 
             data(i)%point(0:j-1) = tmp(0:j-1)
             data(i)%point(j) = key
             data(i)%point(j+1:) = tmp(j:n-1)
          end if
          deallocate(tmp)
          !
          ! delete
          !
          n = data(i2)%n
          data(i2)%n = data(i2)%n - 1
          if ( data(i2)%n > 0) then
             j = found_point(data(i2)%point(:),n, key)
             allocate(tmp(0:n-1))
             tmp(0:n-1) = data(i2)%point(0:n-1)
             deallocate( data(i2)%point )
             allocate( data(i2)%point(0:data(i2)%n-1) )
             data(i2)%point(0:j-1) = tmp(0:j-1)
             data(i2)%point(j:) = tmp(j+1:n-1)
             deallocate(tmp)
          else
             deallocate( data(i2)%point )
          end if
       end if
    end if
    return
  end subroutine insert_delete

  function found_point(a,n,key) result(ok)
    integer :: n
    integer :: a(0:n-1)
    integer :: key
    integer :: ng 
    integer :: ok
    integer :: mid
    ng = -1
    ok = n
    do while( abs(ok-ng) > 1) 
       mid = (ok + ng)/2
       if (isOK(mid,key,a,n)) then
          ok = mid
       else
          ng = mid
       end if
    end do
  end function found_point

  
  function isOK (index, key,a,n) result(out)
    integer :: n
    integer :: a(0:n-1)
    integer :: index, key
    logical :: out
    if ( a(index) >= key) then
       out = .true.
    else 
       out = .false.
    end if
  end function isOK
  subroutine count_char(data,l,r,out)
    type(arr_list) :: data(97:122)
    integer :: l,r
    integer :: out
    integer :: N
    integer :: i, j, tmp
    out = 0
    do i = 97,122
       if ( data(i)%n > 0) then
          j = found_point(data(i)%point(:),data(i)%n,l)
          if ( j < data(i)%N) then
             if ( l<=data(i)%point(j) .and. data(i)%point(j) <= r) then
                out = out + 1
             end if
!!             if ( j+1 < data(i)%N) then
!!                if ( l<=data(i)%point(j) .and. data(i)%point(j) <= r) then
!!                   out = out + 1
!!                   cycle
!!                end if
!!                if ( data(i)%point(j) < l .and. data(i)%point(j+1) <= r) then
!!                   out = out + 1
!!                end if
!!             else
!!             end if
          end if
       end if
    end do
  end subroutine count_char
  
  subroutine count_char2(S,out)
    character(*) :: S
    integer :: out
    integer :: N
    integer :: i, tmp
    integer :: c(97:122)
    N = len_trim(S)
    c(97:122) = 0
    do i = 1,N
       tmp = ichar(S(i:i))
       c (tmp) = c(tmp) + 1
    end do
    out = count(c /=0)
  end subroutine count_char2
end program main