D. Vasya and Types

Codeforces
IDCF88D
Time1000ms
Memory256MB
Difficulty
implementation
English · Original
Chinese · Translation
Formal · Original
Programmer Vasya is studying a new programming language &K*. The &K* language resembles the languages of the C family in its syntax. However, it is more powerful, which is why the rules of the actual C-like languages are unapplicable to it. To fully understand the statement, please read the language's description below carefully and follow it and not the similar rules in real programming languages. There is a very powerful system of pointers on &K* — you can add an asterisk to the right of the existing type _X_ — that will result in new type _X_ * . That is called pointer-definition operation. Also, there is the operation that does the opposite — to any type of _X_, which is a pointer, you can add an ampersand — that will result in a type &_X_, to which refers _X_. That is called a dereference operation. The &K* language has only two basic data types — _void_ and _errtype_. Also, the language has operators _typedef_ and _typeof_. * The operator "_typedef _A_ _B__" defines a new data type _B_, which is equivalent to _A_. _A_ can have asterisks and ampersands, and _B_ cannot have them. For example, the operator _typedef void** ptptvoid_ will create a new type _ptptvoid_, that can be used as _void**_. * The operator "_typeof _A__" returns type of _A_, brought to _void_, that is, returns the type _void**...*_, equivalent to it with the necessary number of asterisks (the number can possibly be zero). That is, having defined the _ptptvoid_ type, as shown above, the _typeof ptptvoid_ operator will return _void**_. An attempt of dereferencing of the _void_ type will lead to an error: to a special data type _errtype_. For _errtype_ the following equation holds true: _errtype*_ = _&errtype_ = _errtype_. An attempt to use the data type that hasn't been defined before that will also lead to the _errtype_. Using _typedef_, we can define one type several times. Of all the definitions only the last one is valid. However, all the types that have been defined earlier using this type do not change. Let us also note that the dereference operation has the lower priority that the pointer operation, in other words &_T_ *  is always equal to _T_. Note, that the operators are executed consecutively one by one. If we have two operators "_typedef &void a_" and "_typedef a* b_", then at first _a_ becomes _errtype_, and after that _b_ becomes _errtype* = errtype_, but **not** _&void* = void_ (see sample 2). Vasya does not yet fully understand this powerful technology, that's why he asked you to help him. Write a program that analyzes these operators. ## Input The first line contains an integer _n_ (1 ≤ _n_ ≤ 100) — the number of operators. Then follow _n_ lines with operators. Each operator is of one of two types: either "_typedef _A_ _B__", or "_typeof _A__". In the first case the _B_ type differs from _void_ and _errtype_ types, and besides, doesn't have any asterisks and ampersands. All the data type names are non-empty lines of no more than 20 lowercase Latin letters. The number of asterisks and ampersands separately in one type in any operator does not exceed 10, however if we bring some types to _void_ with several asterisks, their number may exceed 10. ## Output For every _typeof_ operator print on the single line the answer to that operator — the type that the given operator returned. [samples] ## Note Let's look at the second sample. After the first two queries _typedef_ the _b_ type is equivalent to _void*_, and _c_ — to _void**_. The next query _typedef_ redefines _b_ — it is now equal to _&b = &void* = void_. At that, the _c_ type doesn't change. After that the _c_ type is defined as _&&b* = &&void* = &void = errtype_. It doesn't influence the _b_ type, that's why the next _typedef_ defines _c_ as _&void* = void_. Then the _b_ type is again redefined as _&void = errtype_. Please note that the _c_ type in the next query is defined exactly as _errtype******* = errtype_, and not _&void******* = void******_. The same happens in the last _typedef_.
程序员 Vasya 正在学习一种新的编程语言 &K*。&K* 语言在语法上类似于 C 家族语言。然而,它更强大,因此实际 C 类语言的规则不适用于它。要完全理解题意,请仔细阅读以下语言描述,并严格遵循它,而非实际编程语言中的类似规则。 &K* 语言拥有一个强大的指针系统——你可以在现有类型 #cf_span[X] 的右侧添加一个星号,从而得到新类型 #cf_span[X * ],这称为指针定义操作。同时,也存在一个相反的操作:对于任何指针类型 #cf_span[X],你可以添加一个与号,从而得到类型 #cf_span[&X],它指向 #cf_span[X]。这称为解引用操作。 &K* 语言仅有两种基本数据类型:_void_ 和 _errtype_。此外,语言还包含操作符 _typedef_ 和 _typeof_。 尝试对 _void_ 类型进行解引用会导致错误:产生一个特殊数据类型 _errtype_。对于 _errtype_,以下等式成立:_errtype*_#cf_span[ = ]_&errtype_#cf_span[ = ]_errtype_。尝试使用尚未定义的数据类型也会导致 _errtype_。 使用 _typedef_,我们可以多次定义同一个类型。所有定义中只有最后一个有效。然而,之前使用该类型定义的所有其他类型不会改变。 还需注意,解引用操作的优先级低于指针操作,即 #cf_span[&T * ] 始终等于 #cf_span[T]。 请注意,操作符是按顺序逐个执行的。如果我们有两个操作符 "_typedef &void a_" 和 "_typedef a* b_",那么首先 _a_ 变为 _errtype_,之后 _b_ 变为 _errtype* = errtype_,但 *不是* _&void* = void_(见样例 2)。 Vasya 尚未完全理解这种强大的技术,因此他请求你的帮助。请编写一个程序来分析这些操作符。 第一行包含一个整数 #cf_span[n] (#cf_span[1 ≤ n ≤ 100]) —— 操作符的数量。接下来是 #cf_span[n] 行操作符。每个操作符属于以下两种类型之一:"_typedef #cf_span[A] #cf_span[B]_" 或 "_typeof #cf_span[A]_"。在第一种情况下,#cf_span[B] 类型不同于 _void_ 和 _errtype_,且不包含任何星号和与号。 所有数据类型名称均为不超过 20 个小写拉丁字母的非空字符串。在任一操作符中,单个类型内的星号和与号数量均不超过 10,但若通过多个星号将某些类型化为 _void_,其数量可能超过 10。 对于每个 _typeof_ 操作符,请在单独一行输出该操作符返回的类型。 ## Input 第一行包含一个整数 #cf_span[n] (#cf_span[1 ≤ n ≤ 100]) —— 操作符的数量。接下来是 #cf_span[n] 行操作符。每个操作符属于以下两种类型之一:"_typedef #cf_span[A] #cf_span[B]_" 或 "_typeof #cf_span[A]_"。在第一种情况下,#cf_span[B] 类型不同于 _void_ 和 _errtype_,且不包含任何星号和与号。所有数据类型名称均为不超过 20 个小写拉丁字母的非空字符串。在任一操作符中,单个类型内的星号和与号数量均不超过 10,但若通过多个星号将某些类型化为 _void_,其数量可能超过 10。 ## Output 对于每个 _typeof_ 操作符,请在单独一行输出该操作符返回的类型。 [samples] ## Note 让我们看第二个样例。 在前两个 _typedef_ 查询后,_b_ 类型等价于 _void*_,_c_ 等价于 _void**_。 下一个 _typedef_ 重新定义了 _b_ —— 它现在等于 _&b = &void* = void_。此时,_c_ 类型不变。 之后,_c_ 类型被定义为 _&&b* = &&void* = &void = errtype_。这不会影响 _b_ 类型,因此下一个 _typedef_ 将 _c_ 定义为 _&void* = void_。 然后,_b_ 类型再次被重新定义为 _&void = errtype_。 请注意,下一个查询中 _c_ 类型被定义为 _errtype******* = errtype_,而非 _&void******* = void******_。最后一个 _typedef_ 也是如此。
**Definitions:** - Basic types: $\texttt{void}$, $\texttt{errtype}$ - Pointer operation: For any type $T$, $T^*$ is the pointer type to $T$ - Dereference operation: For any pointer type $T = S^*$, $\&T = S$; if $T$ is not a pointer (i.e., $T = \texttt{void}$ or $T = \texttt{errtype}$), then $\&T = \texttt{errtype}$ - Special rule: $\texttt{errtype}^* = \&\texttt{errtype} = \texttt{errtype}$ - Precedence: $\&T^* = T$ (dereference has lower precedence than pointer; i.e., $\& (T^*) = T$) - Typedef: Each `typedef A B` defines alias $B$ to be the normalized form of type $A$. Previous definitions using $B$ are unaffected. - Type resolution: All types are resolved recursively to their base form using the above rules. Any undefined identifier resolves to $\texttt{errtype}$ **Given:** - $n$ operators ($1 \leq n \leq 100$), each of form: - `typedef A B`: define alias $B := \text{normalize}(A)$ - `typeof A`: output $\text{normalize}(A)$ - Type expressions $A$ consist of: - Base identifiers (lowercase letters, $\leq 20$ chars) - Prefixes of `&` (dereference) and suffixes of `*` (pointer), at most 10 of each per operator - Type names in `typedef` (i.e., $B$) are simple identifiers (no `&` or `*`), and not `void` or `errtype` **Normalization function $\text{normalize}(T)$:** - Parse $T$ into a sequence of $k$ `&` operators and $m$ `*` operators, and a base identifier $X$ (i.e., $T = \&^k (X^{*m})$) - Apply the precedence rule: $\&^k (X^{*m}) = X^{*(m - k)}$ if $m \geq k$, else $\texttt{errtype}^{*(k - m)} = \texttt{errtype}$ (since $\texttt{errtype}^* = \texttt{errtype}$) - But recursively resolve $X$: - If $X$ is `void` or `errtype`: use directly - Else if $X$ is a defined alias: recursively normalize its current definition - Else: $X$ is undefined → result is $\texttt{errtype}$ - Then apply the net pointer/dereference: $k$ `&` and $m$ `*` → net $p = m - k$ - If $p \geq 0$: result is $X_{\text{norm}}^{*p}$ - If $p < 0$: result is $\texttt{errtype}$ (because $\&\texttt{void} = \texttt{errtype}$, and $\&\texttt{errtype} = \texttt{errtype}$) - Special: If at any point the base resolves to $\texttt{errtype}$, then entire expression becomes $\texttt{errtype}$, regardless of pointer count **Objective:** For each `typeof A` operator, output $\text{normalize}(A)$ **State:** - Maintain a dictionary $D$: mapping alias names to their normalized type expressions (as base + net pointer count, or $\texttt{errtype}$) **Algorithm per operator:** 1. Parse operator type 2. If `typedef A B`: - Compute $T = \text{normalize}(A)$ - Update $D[B] = T$ 3. If `typeof A`: - Compute $T = \text{normalize}(A)$ - Output $T$ **Type representation:** - Represent a normalized type as: - $\texttt{errtype}$, or - $(X, p)$ where $X$ is a base type (either `void` or an alias name), and $p \in \mathbb{Z}$ is net pointer count (number of `*` minus number of `&`), with $p \geq 0$; if $p < 0$, result is $\texttt{errtype}$ - But: if base $X$ resolves to $\texttt{errtype}$, then entire type is $\texttt{errtype}$, regardless of $p$ **Final output format:** - For each `typeof`: print the string representation of the normalized type: - $\texttt{errtype}$ - or $X$ if $p = 0$ - or $X$ followed by $p$ asterisks if $p > 0$ (e.g., `void***`) Note: All alias definitions are resolved dynamically at time of use (latest definition is active, prior uses are not retroactively updated).
Samples
Input #1
5
typedef void* ptv
typeof ptv
typedef &&ptv node
typeof node
typeof &ptv
Output #1
void*
errtype
void
Input #2
17
typedef void* b
typedef b* c
typeof b
typeof c
typedef &b b
typeof b
typeof c
typedef &&b* c
typeof c
typedef &b* c
typeof c
typedef &void b
typeof b
typedef b******* c
typeof c
typedef &&b* c
typeof c
Output #2
void*
void**
void
void**
errtype
void
errtype
errtype
errtype
API Response (JSON)
{
  "problem": {
    "name": "D. Vasya and Types",
    "description": {
      "content": "Programmer Vasya is studying a new programming language &K*. The &K* language resembles the languages of the C family in its syntax. However, it is more powerful, which is why the rules of the actual ",
      "description_type": "Markdown"
    },
    "platform": "Codeforces",
    "limit": {
      "time_limit": 1000,
      "memory_limit": 262144
    },
    "difficulty": "None",
    "is_remote": true,
    "is_sync": true,
    "sync_url": null,
    "sign": "CF88D"
  },
  "statements": [
    {
      "statement_type": "Markdown",
      "content": "Programmer Vasya is studying a new programming language &K*. The &K* language resembles the languages of the C family in its syntax. However, it is more powerful, which is why the rules of the actual ...",
      "is_translate": false,
      "language": "English"
    },
    {
      "statement_type": "Markdown",
      "content": "程序员 Vasya 正在学习一种新的编程语言 &K*。&K* 语言在语法上类似于 C 家族语言。然而,它更强大,因此实际 C 类语言的规则不适用于它。要完全理解题意,请仔细阅读以下语言描述,并严格遵循它,而非实际编程语言中的类似规则。\n\n&K* 语言拥有一个强大的指针系统——你可以在现有类型 #cf_span[X] 的右侧添加一个星号,从而得到新类型 #cf_span[X * ],这称为指针定义操...",
      "is_translate": true,
      "language": "Chinese"
    },
    {
      "statement_type": "Markdown",
      "content": "**Definitions:**\n- Basic types: $\\texttt{void}$, $\\texttt{errtype}$\n- Pointer operation: For any type $T$, $T^*$ is the pointer type to $T$\n- Dereference operation: For any pointer type $T = S^*$, $\\&...",
      "is_translate": false,
      "language": "Formal"
    }
  ]
}
Full JSON Raw Segments