题目背景

小 H 同学发现,他维护的存储系统经常出现有人用机器学习的训练数据把空间占满的问题,十分苦恼。

查找了一阵资料后,他想要在文件系统中开启配额限制,以便能够精确地限制大家在每个目录中最多能使用的空间。

文件系统概述

文件系统,是一种树形的文件组织和管理方式。

在文件系统中,文件是指用名字标识的文件系统能够管理的基本对象,分为普通文件目录文件两种,目录文件可以被简称为目录。目录中有一种特殊的目录被叫做根目录

除了根目录外,其余的文件都有名字,称为文件名

合法的文件名是一个由若干数字([0-9])、大小写字母([A-Za-z])组成的非空字符串。

普通文件中含有一定量的数据,占用存储空间;目录不占用存储空间。

文件和目录之间存在含于关系。

上述概念满足下列性质:

  1. 有且仅有一个根目录
  2. 对于除根目录以外的文件,都含于且恰好含于一个目录;
  3. 含于同一目录的文件,它们的文件名互不相同;
  4. 对于任意不是根目录的文件 f,若 f 不含于根目录,那么存在有限个目录 d1,d2,…,dn,

    使得 f 含于 d1,d1 含于 d2,…,dn 含于根目录。

结合性质 4 和性质 2 可知,性质 4 中描述的有限多个目录,即诸 di,是唯一的。再结合性质 3,我们即可通过从根目录开始的一系列目录的序列,来唯一地指代一个文件。

我们记任意不是根目录且不含于根目录的文件 f 的文件名是 Nf,那么 f 的路径 是:‘/′+Ndn+‘/′+⋯+Nd1+‘/′+Nf,其中符号 + 表示字符串的连接;对于含于根目录的文件 f,它的路径是:‘/′+Nf;根目录的路径是:‘/′。不符合上述规定的路径都是非法的。例如:/A/B是合法路径,但 /A//B/A/A/A/B 都不是合法路径。

若文件 f 含于目录 d,我们也称 f 是 d 的孩子文件。d 是 f 的双亲目录

我们称文件 f 是目录 d 的后代文件,如果满足:(1) f 是 d 的孩子文件,或(2)f 含于 d 的后代文件。

如图所示,该图中绘制的文件系统共有 8 个文件。其中,方形表示目录文件,圆形表示普通文件,它们之间的箭头表示含于关系。在表示文件的形状上的文字是其文件名;各个形状的左上方标记了序号,以便叙述。在该文件系统中,文件 5 含于文件 2,文件 5 是文件 2 的孩子文件,文件 5 也是文件 2 的后代文件。文件 8 是文件 2 的后代文件,但不是文件 2 的孩子文件。文件 8 的路径是 /D1/D1/F2

配额概述

配额是指对文件系统中所含普通文件的总大小的限制。

对于每个目录 d,都可以设定两个配额值:目录配额后代配额

我们称目录配额 LDd 是满足的,当且仅当 d 的孩子文件中,全部普通文件占用的存储空间之和不大于该配额值。

我们称后代配额 LRd是满足的,当且仅当 d 的后代文件中,全部普通文件占用的存储空间之和不大于该配额值。

我们称文件系统的配额是满足的,当且仅当该文件系统中所有的配额都是满足的。

很显然,若文件系统中仅存在目录,不存在普通文件,那么该文件系统的配额一定是满足的。随着配额和文件的创建,某个操作会使文件系统的配额由满足变为不满足,这样的操作会被拒绝。例如:试图设定少于目前已有文件占用空间的配额值,或者试图创建超过配额值的文件。

题目描述

在本题中,假定初始状态下,文件系统仅包含根目录。你将会收到若干对文件系统的操作指令。对于每条指令,你需要判断该指令能否执行成功,对于能执行成功的指令,在成功执行该指令后,文件系统将会被相应地修改。对于不能执行成功的指令,文件系统将不会发生任何变化。你需要处理的指令如下:

创建普通文件

创建普通文件指令的格式如下:

C <file path> <file size>

创建普通文件的指令有两个参数,是空格分隔的字符串和一个正整数,分别表示需要创建的普通文件的路径和文件的大小。

对于该指令,若路径所指的文件已经存在,且也是普通文件的,则替换这个文件;若路径所指文件已经存在,但是目录文件的,则该指令不能执行成功。

当路径中的任何目录不存在时,应当尝试创建这些目录;若要创建的目录文件与已有的同一双亲目录下的孩子文件中的普通文件名称重复,则该指令不能执行成功。

另外,还需要确定在该指令的执行是否会使该文件系统的配额变为不满足,如果会发生这样的情况,则认为该指令不能执行成功,反之则认为该指令能执行成功。

移除文件

移除文件指令的格式如下:

R <file path>

移除文件的指令有一个参数,是字符串,表示要移除的文件的路径。若该路径所指的文件不存在,则不进行任何操作。若该路径所指的文件是目录,则移除该目录及其所有后代文件。

在上述过程中被移除的目录(如果有)上设置的配额值也被移除。

该指令始终认为能执行成功。

设置配额值

Q <file path> <LD> <LR>

设置配额值的指令有三个参数,是空格分隔的字符串和两个非负整数,分别表示需要设置配额值的目录的路径、目录配额和后代配额。

该指令表示对所指的目录文件,分别设置目录配额和后代配额。若路径所指的文件不存在,或者不是目录文件,则该指令执行不成功。

若在该目录上已经设置了配额,则将原配额值替换为指定的配额值。

特别地,若配额值为 0,则表示不对该项配额进行限制。若在应用新的配额值后,该文件系统配额变为不满足,那么该指令执行不成功。

输入格式

从标准输入读入数据。

输入的第一行包含一个正整数 n,表示需要处理的指令条数。

输入接下来会有 n 行,每一行一个指令。指令的格式符合前述要求。输入数据保证:对于所有指令,输入的路径是合法路径;对于创建普通文件和移除文件指令,输入的路径不指向根目录。

输出格式

输出到标准输出。

输出共有 n 行,表示相应的操作指令是否执行成功。若成功执行,则输出字母 Y;否则输出 N

样例1输入

10
C /A/B/1 1024
C /A/B/2 1024
C /A/B/1/3 1024
C /A 1024
R /A/B/1/3
Q / 0 1500
C /A/B/1 100
Q / 0 1500
R /A/B
Q / 0 1

样例1输出

Y
Y
N
N
Y
N
Y
Y
Y
Y

样例1解释

输入总共有 10 条指令。

其中前两条指令可以正常创建两个普通文件。

第三条指令试图创建 /A/B/1/3,但是 /A/B/1 已经存在,且不是目录,而是普通文件,不能再进一步创建孩子文件,因此执行不成功。

第四条指令试图创建 /A,但是 /A 已经存在,且是目录,因此执行不成功。

第五条指令试图删除 /A/B/1/3,由于该文件不存在,因此不对文件系统进行修改,但是仍然认为执行成功。

第六条指令试图在根目录增加后代配额限制,但此时,文件系统中的文件总大小是 2048,因此该限制无法生效,执行不成功。

第七条指令试图创建文件 /A/B/1,由于 /A/B/1 已经存在,且是普通文件,因此该指令实际效果是将原有的该文件替换。

此时文件总大小是 1124,因此第八条指令就可以执行成功了。

第九条指令递归删除了 /A/B 目录和它的所有后代文件。

此时文件系统中已经没有普通文件,因此第十条命令可以执行成功。

样例2输入

9
Q /A/B 1030 2060
C /A/B/1 1024
C /A/C/1 1024
Q /A/B 1024 0
Q /A/C 0 1024
C /A/B/3 1024
C /A/B/D/3 1024
C /A/C/4 1024
C /A/C/D/4 1024

样例2输出

N
Y
Y
Y
Y
N
Y
N
N

样例2解释

输入共有 9 条指令。

第一条指令试图为 /A/B 创建配额规则,然而该目录并不存在,因此执行不成功。

接下来的两条指令创建了两个普通文件。再接下来的两条指令分别在目录 /A/B/A/C 创建了两个配额规则。其中前者是目录配额,后者是后代配额。

接下来的两条指令,创建了两个文件。其中,/A/B/3 超出了在 /A/B 的目录配额,因此执行不成功;但 /A/B/D/3

不受目录配额限制,因此执行成功。

最后两条指令,创建了两个文件。虽然在 /A/C没有目录配额限制,但是无论是 /A/C 下的孩子文件还是后代文件,都受到后代配额的限制,因此两条指令执行都不成功。

子任务

本题目各个测试点的数据规模如下:

表格中,目录层次是指各指令中出现的路径中,/ 字符的数目。

所有输入的数字均不超过\(10^{18}\)。

转载:https://blog.csdn.net/weixin_43693379/article/details/112725802

代码

#include<iostream>
#include<algorithm>
#include<map>
#include<vector> using namespace std;
const int N = 5e6 + 10;
typedef long long ll;
//LDd目录配额,LRd后代配额,LD现有目录额,LR现有后代额
struct node {
map<string, int> branch; //每个目录节点的孩子节点(包括目录节点和普通文件)
int isleaf; //是否是普通文件
ll LDd, LRd; //目录配额,后代配额
ll LD, LR; //现有目录,后代
ll val; //普通文件的大小
int fa; //父节点编号
};
node tr[N];
int vex; //有效文件名所属编号
string getindex(string &s, int &num) //截取一个合法文件名
{
string res = "";
int i;
for (i = num; s[i] != '/' && i < s.size(); i++)
res += s[i];
num = i + 1;
return res;
} vector<pair<int, string> > v; void del() //若插入失败,对本次前面所有插入操作进行撤销
{ for (int i = 0; i < v.size(); i++) { int a = v[i].first;
string b = v[i].second;
tr[a].branch.erase(tr[a].branch.find(b));
}
} bool insert(string s, ll size) //文件插入
{
v.clear();
int num = 1, fa = 0, init = vex, len = s.size();
while (num < len) //直至截取最后一个文件名
{
string ss = getindex(s, num);
//如果要创建的目录文件与已有的同一双亲目录下的孩子文件中的普通文件名称重复,则该指令不能执行成功
if (tr[fa].branch[ss] && tr[tr[fa].branch[ss]].isleaf && num < len) {
vex = init;
del();
return false;
}
int id, flag = 0;
if (tr[fa].branch[ss])
id = tr[fa].branch[ss], flag = 1;
else //若不存在该目录/普通文件,进行创建
id = ++vex, tr[vex].isleaf = 0, tr[vex].fa = fa, tr[vex].LDd = 0, tr[vex].LRd = 0, tr[fa].branch[ss] = vex, v.push_back(
make_pair(fa, ss));
if (num < len)
fa = id;
if (num >= len) //遍历至叶子文件时
{
ll extra; //需要更新的文件值
if (flag) //叶子文件如果已存在
{
if (!tr[id].isleaf) //如果是个目录文件,插入失败
{
vex = init;
del();
return false;
}
extra = size - tr[id].val;
} else
extra = size;
if (tr[fa].LD + extra > tr[fa].LDd && tr[fa].LDd) //如果双亲目录的目录配额存在且因为插入该文件超额,插入失败
{
vex = init;
del();
return false;
}
tr[id].val = size;
tr[id].LR = size;
tr[id].isleaf = 1;
for (int r = fa; r != -1; r = tr[r].fa)
if (tr[r].LR + extra > tr[r].LRd && tr[r].LRd) //如果后代配额存在且会因为插入此文件超额,插入失败
{
vex = init;
del();
return false;
}
for (int r = fa; r != -1; r = tr[r].fa) //插入成功,更新整条路径
tr[r].LR += extra;
tr[fa].LD += extra;
} }
return true;
} void remove(string s) //移除文件
{
int num = 1, fa = 0, len = s.size();
while (num < len) {
string ss = getindex(s, num);
if (!tr[fa].branch[ss]) return; //若路径不存在,直接返回
int id = tr[fa].branch[ss];
if (num < len)
fa = id;
if (num < len && tr[id].isleaf) //如果路径不合法,返回
return;
if (num >= len) {
tr[fa].branch.erase(tr[fa].branch.find(ss)); //删除与双亲节点之间的路径
for (int r = fa; r != -1; r = tr[r].fa) //更新前面祖宗节点的后代配额
tr[r].LR -= tr[id].LR;
if (tr[id].isleaf) //若删除节点为叶子节点,更新双亲节点的目录配额
tr[fa].LD -= tr[id].LR;
}
}
} bool set(string s, ll ld, ll lr) //设置配置额
{
if (s.size() == 1) //配置根目录
{
int id = 0;
if ((ld < tr[id].LD && ld) || (lr < tr[id].LR && lr)) //如果配置额小于当前已存在的文件大小,配置失败
return false;
tr[id].LRd = lr;
tr[id].LDd = ld;
return true;
}
int num = 1, fa = 0, len = s.size();
while (num < len) {
string ss = getindex(s, num);
if (!tr[fa].branch[ss]) return false; //如果路径不存在,配置失败
int id = tr[fa].branch[ss];
fa = id;
if (tr[id].isleaf) return false; //如果配置的不是目录文件,配置失败
if (num >= len) {
if ((ld < tr[id].LD && ld) || (lr < tr[id].LR && lr)) //如果配置额小于当前已存在的文件大小,配置失败
return false;
tr[id].LRd = lr;
tr[id].LDd = ld; //修改额度,配置成功
return true;
}
}
} int main() {
ios::sync_with_stdio(false);
cin.tie(0);
string s;
int n;
cin >> n;
tr[0].fa = -1;
tr[0].isleaf = 0;
tr[0].LDd = 0;
tr[0].LRd = 0;
while (n--) {
bool flag = true;
char order;
cin >> order;
ll ld, lr, size;
switch (order) {
case 'C':
cin >> s >> size;
flag = insert(s, size);
break;
case 'R':
cin >> s;
remove(s);
break;
case 'Q':
cin >> s >> ld >> lr;
flag = set(s, ld, lr);
break;
}
if (flag)
cout << "Y" << '\n';
else
cout << "N" << '\n';
}
return 0;
}

CCF CSP 202012-3 文件配额的更多相关文章

  1. CCF CSP 201604-3 路径解析

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201604-3 路径解析 问题描述 在操作系统中,数据通常以文件的形式存储在文件系统中.文件系 ...

  2. CCF CSP/CCSP报名费优惠的方法以及常见疑问

    目录 1. 本文地址 2. 认证作用 2.1. 高校认可 2.2. 赛事认可 2.3. 企业认可 3. 报名费价格及获取优惠的方法 3.1. CCF CSP 3.2. CCF CCSP 4. 语言与I ...

  3. CCF CSP 认证

    参加第八次CCF CSP认证记录 代码还不知道对不对,过两天出成绩. 成绩出来了,310分. 100+100+100+10+0: 考试13:27开始,17:30结束,提交第4题后不再答题,只是检查前四 ...

  4. CCF CSP 201609-2 火车购票

    题目链接:http://118.190.20.162/view.page?gpid=T46 问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配. 假设一节车厢有20排.每一排 ...

  5. CCF CSP 201703-3 Markdown

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201703-3 Markdown 问题描述 Markdown 是一种很流行的轻量级标记语言(l ...

  6. CCF CSP 201703

    CCF CSP 2017·03 做了一段时间的CCF CSP试题,个人感觉是这样分布的 A.B题基本纯暴力可满分 B题留心数据范围 C题是个大模拟,留心即可 D题更倾向于图论?(个人做到的D题基本都是 ...

  7. CCF CSP 201312-3 最大的矩形

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201312-3 最大的矩形 问题描述 在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i( ...

  8. CCF CSP 201609-3 炉石传说

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201609-3 炉石传说 问题描述 <炉石传说:魔兽英雄传>(Hearthston ...

  9. CCF CSP 201403-3 命令行选项

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201403-3 命令行选项 问题描述 请你写一个命令行分析程序,用以分析给定的命令行里包含哪些 ...

  10. CCF CSP 201709-4 通信网络

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201709-4 通信网络 问题描述 某国的军队由N个部门组成,为了提高安全性,部门之间建立了M ...

随机推荐

  1. JS常见面试题,看看你都会多少?

    1. 如何在ES5环境下实现let 这个问题实质上是在回答let和var有什么区别,对于这个问题,我们可以直接查看babel转换前后的结果,看一下在循环中通过let定义的变量是如何解决变量提升的问题 ...

  2. [论文阅读笔记] node2vec Scalable Feature Learning for Networks

    [论文阅读笔记] node2vec:Scalable Feature Learning for Networks 本文结构 解决问题 主要贡献 算法原理 参考文献 (1) 解决问题 由于DeepWal ...

  3. 进制及其字符串之间互转——C#

    本文介绍进制数转进制数,及每个进制对应的字符串 一.首先进制数转进制数(int-->int) 1.二进制数与十进制数互转: (1)二进制数转十进制数:还没找到 (2)十进制数转二进制数:目前还没 ...

  4. JAVA_基础反射机制

    Java反射机制概述 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期 借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内 部属性及方法. 加 ...

  5. Web Service 服务无法连接Oracle数据库

    这个问题之前部署就遇到过,但是后来忘了,所以记录一下吧. 我部署Web Service服务的时候,服务没法正常运行,与Oracle数据库无法正常通信. 检查了数据库连接字没有任何问题,写了个测试接口, ...

  6. LeetCode430 扁平化多级双向链表

    您将获得一个双向链表,除了下一个和前一个指针之外,它还有一个子指针,可能指向单独的双向链表.这些子列表可能有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示. 扁平化列表,使所有结点 ...

  7. 【Linux】linux的所有文件分类解析

    今天看书的时候,无意间看到/dev/文件夹,以前没注意,今天去看了下发现,很多文件的开头文件属性都是一些不怎么见到的 常见的是   -     这个是代表文件,可以vim编辑的 d     这个是代表 ...

  8. 类转json的基类实现

    类转json的基类实现 项目地址 github地址 实现原理 使用反射获取类的属性名和属性内容.具体原理可以自己查一下资料 对一个类调用getClass().getDeclaredFields()可以 ...

  9. STM32F207时钟系统解析

    在前几天的文章<晶振原理解析>中介绍了晶振如何产生时钟的,板子使用的是25M无源晶振,下文将介绍STM32F207的时钟系统如何将25M晶振时钟转换为120M系统主频时钟的. 01.时钟系 ...

  10. 干电池升压5V,功耗10uA

    PW5100干电池升压5V芯片 输出电容: 所以为了减小输出的纹波,需要比较大的输出电容值.但是输出电容过大,就会使得系统的 反应时间过慢,成本也会增加.所以建议使用一个 22uF 的电容,或者两个 ...