{ }codelper
布尔值乘方P0132

权限位检查器

某个服务把用户的权限存放在一个整数(权限值)里。
第一行给出权限数 k,第二行给出 k 个权限名,用一个空格分开。
从前往后称为第 0、第 1、…、第 (k-1) 个权限,第 i 个权限对应值 2^i。
把权限值写成二进制时,2^i 位是 1 就有第 i 个权限,是 0 就没有。
例如权限依次是 read write delete admin 时,权限值 5(= 1 + 4)拥有 read 和 delete。
第三行给出查询数 q,接下来 q 行每行一个查询。
查询由权限值、方式和一个以上的权限名组成,用一个空格分开。
方式为 "all" 时拥有列出的全部权限才通过,为 "any" 时至少拥有一个就通过,为 "none" 时一个都没有才通过。
对每个查询,通过就输出 "是",否则输出 "否",各占一行。

输入
1 ≤ k ≤ 30,权限名是 1~10 个小写英文字母且互不相同,1 ≤ q ≤ 1,000,方式是 all、any、none 之一,0 ≤ 权限值 < 2^k,查询中的权限名都在列表中且互不相同
输出
每个查询输出一行 "是" 或 "否"
样例 1 · stdin
4
read write delete admin
5
5 all read delete
5 all read write
2 any read delete
2 any write admin
8 none read write
stdout
是
否
否
是
是
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

第 i 个权限的值 2^i,在 C、C++、Java、C#、JS、TS、Python 中都可以用位移 1 << i 精确得到。
Python 2 ** i、Java/C# Math.pow/Math.Pow(返回浮点数)也可以,但对 2 的幂,保持整数的 1 << i 最干净。
2 ^ i 不是幂而是按位异或(2 ^ 0 = 2,2 ^ 1 = 3),不报错却会检查错误的权限。
Craykon 既没有位运算也没有幂运算符,所以从 1 开始用 值 *= 2 逐次翻倍建表,再用 数学.商(mask, 值) % 2 == 1(除以 2^i 的商是奇数)判断那一位是否为 1。
运算符优先级陷阱:在 C、C++、JS、TS 中 mask & bit != 0 会先算 !=,变成 mask & 1,得到错误答案。
一定要加括号写成 (mask & bit) != 0。
在 Java、C# 中同样的式子会因为混用 int 和 bool 而编译错误,马上就能发现;Python 的 & 优先于比较,不加括号也正确。
布尔值陷阱:C、C++ 把非 0 整数当作真,但 Java、C# 不接受 if (mask & bit),必须是真正的 boolean。
C 用 <stdbool.h> 的 bool,Craykon 用 真/假。
all 从真开始用 && 累积,any 从假开始用 || 累积。
Python 的 all/any、JS/TS 的 every/some 做的是同样的事。
none 是「不是 any」,而不是「不是 all」;只拥有部分权限的用户会让两者不同。
1 << i 是 32 位 int,i 为 31 时是负数;Java、C#、JS、TS 只取移位数除以 32 的余数,所以 1 << 32 是 1(C、C++ 中是未定义行为)。
权限超过 32 个就需要 64 位整数(1L << i、1LL << i,JS/TS 用 BigInt),但本题 k ≤ 30,所以没问题。

Pythonmain.py · 15 行
k = int(input())names = input().split()bit_of = {name: 1 << i for i, name in enumerate(names)}  # i번째 권한의 값은 2^iq = int(input())for _ in range(q):    t = input().split()    mask, mode, wanted = int(t[0]), t[1], t[2:]    has = [mask & bit_of[w] != 0 for w in wanted]  # Python은 &가 !=보다 먼저 계산된다    if mode == "all":        ok = all(has)    elif mode == "any":        ok = any(has)    else:  # none        ok = not any(has)    print("是" if ok else "否")
JavaScriptmain.js · 16 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const names = lines[1].trim().split(/\s+/);const bitOf = new Map(names.map((name, i) => [name, 1 << i])); // i번째 권한의 값은 2^iconst q = Number(lines[2]);const out = [];for (let j = 0; j < q; j++) {  const [m, mode, ...wanted] = lines[3 + j].trim().split(/\s+/);  const mask = Number(m);  const has = wanted.map((w) => (mask & bitOf.get(w)) !== 0); // 괄호 필수: !==가 &보다 먼저다  let ok;  if (mode === "all") ok = has.every((h) => h);  else if (mode === "any") ok = has.some((h) => h);  else ok = !has.some((h) => h); // none  out.push(ok ? "是" : "否");}console.log(out.join("\n"));
TypeScriptmain.ts · 17 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const names: string[] = lines[1].trim().split(/\s+/);const bitOf = new Map<string, number>(names.map((name, i) => [name, 1 << i])); // i번째 권한의 값은 2^iconst q = Number(lines[2]);const out: string[] = [];for (let j = 0; j < q; j++) {  const [m, mode, ...wanted] = lines[3 + j].trim().split(/\s+/);  const mask = Number(m);  const has: boolean[] = wanted.map((w) => (mask & bitOf.get(w)!) !== 0); // 괄호 필수: !==가 &보다 먼저다  let ok: boolean;  if (mode === "all") ok = has.every((h) => h);  else if (mode === "any") ok = has.some((h) => h);  else ok = !has.some((h) => h); // none  out.push(ok ? "是" : "否");}console.log(out.join("\n"));
JavaMain.java · 30 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int k = Integer.parseInt(in.readLine().trim());        String[] names = in.readLine().trim().split("\\s+");        Map<String, Integer> bitOf = new HashMap<>();        for (int i = 0; i < k; i++) bitOf.put(names[i], 1 << i); // i번째 권한의 값은 2^i        int q = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int j = 0; j < q; j++) {            String[] t = in.readLine().trim().split("\\s+");            int mask = Integer.parseInt(t[0]);            String mode = t[1];            boolean all = true, any = false;            for (int w = 2; w < t.length; w++) {                boolean has = (mask & bitOf.get(t[w])) != 0; // Java는 int를 참·거짓으로 쓸 수 없어 != 0이 꼭 필요하다                all = all && has;                any = any || has;            }            boolean ok = mode.equals("all") ? all : mode.equals("any") ? any : !any;            out.append(ok ? "是" : "否").append('\n');        }        System.out.print(out);    }}
C#Main.cs · 32 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        int k = int.Parse(Console.ReadLine());        string[] names = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        var bitOf = new Dictionary<string, int>();        for (int i = 0; i < k; i++) bitOf[names[i]] = 1 << i; // i번째 권한의 값은 2^i        int q = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int j = 0; j < q; j++)        {            string[] t = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            int mask = int.Parse(t[0]);            string mode = t[1];            bool all = true, any = false;            for (int w = 2; w < t.Length; w++)            {                bool has = (mask & bitOf[t[w]]) != 0; // C#은 int를 bool로 쓸 수 없어 != 0이 꼭 필요하다                all = all && has;                any = any || has;            }            bool ok = mode == "all" ? all : mode == "any" ? any : !any;            sb.Append(ok ? "是" : "否").Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 34 行
#include <iostream>#include <sstream>#include <string>#include <unordered_map> int main() {    int k;    std::cin >> k;    std::unordered_map<std::string, int> bitOf;    for (int i = 0; i < k; i++) {        std::string name;        std::cin >> name;        bitOf[name] = 1 << i; // i번째 권한의 값은 2^i    }    int q;    std::cin >> q;    std::string line;    std::getline(std::cin, line); // q 뒤에 남은 줄바꿈    for (int j = 0; j < q; j++) {        std::getline(std::cin, line);        std::istringstream ss(line);        int mask;        std::string mode, w;        ss >> mask >> mode;        bool all = true, any = false;        while (ss >> w) {            bool has = (mask & bitOf[w]) != 0; // 괄호 필수: != 가 & 보다 먼저 계산된다            all = all && has;            any = any || has;        }        bool ok = mode == "all" ? all : mode == "any" ? any : !any;        std::cout << (ok ? "是" : "否") << '\n';    }}
Cmain.c · 32 行
#include <stdbool.h>#include <stdio.h>#include <string.h> int main(void) {    int k;    char names[30][11];    scanf("%d", &k);    for (int i = 0; i < k; i++) scanf("%10s", names[i]);    int q;    scanf("%d", &q);    for (int j = 0; j < q; j++) {        int mask;        char mode[5];        scanf("%d %4s", &mask, mode);        bool all = true, any = false;        /* 이름들은 줄 끝까지 이어진다: 다음 글자가 줄바꿈이면 멈춘다 */        int c;        while ((c = getchar()) == ' ') {            char w[11];            scanf("%10s", w);            int i = 0;            while (strcmp(names[i], w) != 0) i++;            bool has = (mask & (1 << i)) != 0; /* 괄호 필수: != 가 & 보다 먼저 계산된다 */            all = all && has;            any = any || has;        }        bool ok = strcmp(mode, "all") == 0 ? all : strcmp(mode, "any") == 0 ? any : !any;        puts(ok ? "是" : "否");    }    return 0;}
Craykonmain.cray · 44 行
// 크레이콘에는 비트 연산이 없다: mask를 2^i로 나눈 몫이 홀수이면 i번째 비트가 켜져 있다켜졌나(mask, 값){    返回 数学.商(mask, 값) % 2 == 1;} k = 控制台.读行().转数字();names = 控制台.读行().分割();bitOf = 字典();값 = 1;循环 (i = 0; i < k; i++){    bitOf[names[i]] = 값; // i번째 권한의 값은 2^i: 1에서 시작해 두 배씩    값 *= 2;}q = 控制台.读行().转数字();循环 (j = 0; j < q; j++){    t = 控制台.读行().分割();    mask = t[0].转数字();    mode = t[1];    모두 = 真;    하나라도 = 假;    循环 (w = 2; w < t.数量; w++)    {        있음 = 켜졌나(mask, bitOf[t[w]]);        모두 = 모두 && 있음;        하나라도 = 하나라도 || 있음;    }    ok = 假;    如果 (mode == "all")    {        ok = 모두;    }    否则 如果 (mode == "any")    {        ok = 하나라도;    }    否则    {        ok = !하나라도;    }    控制台.输出(ok ? "是" : "否");}