Improve freeze performance (#32)

diff --git a/tomli/_parser.py b/tomli/_parser.py
index bbaf620..51bc56e 100644
--- a/tomli/_parser.py
+++ b/tomli/_parser.py
@@ -148,12 +148,18 @@
 
 class NestedDict:
     def __init__(self) -> None:
+        # The parsed content of the TOML document
         self.dict: Dict[str, Any] = {}
         # Keep track of keys that have been explicitly set
         self._explicitly_created: Set[Key] = set()
-        # Keep track of keys that hold immutable values. Immutability
-        # applies recursively to sub-structures.
-        self._frozen: Set[Key] = set()
+        # Keep track of keys that hold immutable values. Immutability applies
+        # recursively to sub-structures. The structure is as follows:
+        # {
+        #   "key1": {"frozen": True, "nested": {...}},
+        #   "key2": {"frozen": False, "nested": {...}},
+        # }
+        # where "nested" values are structured the same as the root dict.
+        self._frozen: Dict[str, dict] = {}
 
     def get_or_create_nest(
         self,
@@ -192,13 +198,25 @@
         return key in self._explicitly_created
 
     def is_frozen(self, key: Key) -> bool:
-        for frozen_space in self._frozen:
-            if key[: len(frozen_space)] == frozen_space:
-                return True
+        container = self._frozen
+        for k in key:  # pragma: no cover  # coverage.py seems broken in py3.10 here
+            if k in container:
+                status_container = container[k]
+                if status_container["frozen"]:
+                    return True
+                container = status_container["nested"]
+                continue
+            break
         return False
 
     def mark_frozen(self, key: Key) -> None:
-        self._frozen.add(key)
+        container = self._frozen
+        key_parent, key_stem = key[:-1], key[-1]
+        for k in key_parent:
+            if k not in container:
+                container[k] = {"frozen": False, "nested": {}}
+            container = container[k]["nested"]
+        container[key_stem] = {"frozen": True, "nested": {}}
 
     def mark_relative_path_explicitly_created(
         self, head_key: Key, rel_key: Key
@@ -210,10 +228,18 @@
         """Recursively unmark explicitly created and frozen statuses in the
         namespace."""
         len_key = len(key)
-        self._frozen = {f for f in self._frozen if f[:len_key] != key}
+        # Reset explicitly created
         self._explicitly_created = {
             e for e in self._explicitly_created if e[:len_key] != key
         }
+        # Reset frozen
+        key_parent, key_stem = key[:-1], key[-1]
+        cont = self._frozen
+        for k in key_parent:
+            if k not in cont:
+                return
+            cont = cont[k]["nested"]
+        cont[key_stem] = {"frozen": False, "nested": {}}
 
 
 def skip_chars(state: State, chars: Iterable[str]) -> None: