-
此题并没有在时间和空间上难为我们,但是完美诠释了汉语的博大精深,有很多细节问题需要注意,是用来熟悉数结构和练习模拟题的好材料。
-
我的思路是先看大佬的思路 😆
-
因为自己对树结构本来也不太熟悉,如果完全自己思考可能会走很多弯路,不如先学习一下大佬的建树思路,然后自己去实现题目要求,后期面对大数据量出现bug不好排查时还可以对比大佬的ac代码,方便快速定位bug。
-
自己动手实现,之后再对比其他人的代码还可以帮助优化自己的编程风格,比如本菜就发现自己在实现executeC()函数时,存在非常严重的“低内聚、高耦合”现象😁
-
ac代码如下,注释中后缀" !!!"的是本人费好大劲才找到的bug,供大家happy一下😄
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
#include <limits.h>
using namespace std;
typedef long long LL;
struct node {
int father;
map<string, int> children;
bool type; //0:目录文件, 1:普通文件
LL size;
LL s1, s2; //s1:目录配额, s2:孩子配额
LL u1, u2; //u1:已使用目录配额, u2:已使用孩子配额
}T[4000010];
vector<pair<int, string> > reback;
int node_num; //记录当前已经使用的节点个数,用于创建新节点时方便找到一个未使用的节点编号
void Reback() {
for (int i = 0; i < reback.size(); i++) {
int t = reback[i].first;
string temp = reback[i].second;
T[t].children.erase(T[t].children.find(temp));
}
}
string executeC() {
string filepath, filename = "", temp = "";
LL size;
cin >> filepath >> size;
int c = 0, l, t=0; //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
for (l = filepath.size() - 1; filepath[l] != '/'; l--);
for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
reback.clear();
int oldnum = node_num;
while (c < l) {
temp = "";
for (++c; filepath[c] != '/'; temp += filepath[c], c++);
if (T[t].children.find(temp) == T[t].children.end()) { //没有这个目录,创建
T[t].children[temp] = node_num; //更新当前节点的孩子节点
T[node_num].father = t;
T[node_num].type = 0;
T[node_num].size = 0;
T[node_num].s1 = T[node_num].s2 = LLONG_MAX / 3;
T[node_num].u1 = T[node_num].u2 = 0;
reback.push_back(make_pair(t, temp)); //记录父节点编号和子节点名字
t = node_num;
node_num++;
}
else {
t = T[t].children[temp];
if (T[t].type) { //存在同名普通文件
node_num = oldnum;
Reback();
return "N";
}
}
}
//路径已遍历结束,对最后的文件名进行处理
if (T[t].children.find(filename) != T[t].children.end()) {
t = T[t].children[filename];
if (!T[t].type) { //存在同名目录文件
node_num = oldnum;
Reback(); //回滚撤销之前创建的新目录文件
return "N";
}
else {
if (T[t].size == size) {
return "Y";
}
else if (T[t].size > size) {
LL changesize = T[t].size - size;
T[t].size = size;
t = T[t].father;
T[t].u1 -= changesize;
T[t].u2 -= changesize;
for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 -= changesize;
return "Y";
}
else {
LL changesize = size - T[t].size;
int f = T[t].father; //注意细节,此处t是同名文件节点啊!!!
if (T[f].s1 - T[f].u1 >= changesize && T[f].s2 - T[f].u2 >= changesize) {
for (int i = T[f].father; i != -1; i = T[i].father) {
if (T[i].s2 - T[i].u2 < changesize) {
node_num = oldnum;
Reback();
return "N";
}
}
T[t].size = size;
t = f;
T[t].u1 += changesize;
T[t].u2 += changesize;
for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 += changesize;
return "Y";
}
node_num = oldnum;
Reback();
return "N";
}
}
}
else {//创建新的普通文件
if (T[t].s1 - T[t].u1 >= size && T[t].s2 - T[t].u2 >= size) {
for (int i = T[t].father; i != -1; i = T[i].father) {
if (T[i].s2 - T[i].u2 < size) {
node_num = oldnum;
Reback();
return "N";
}
}
T[t].u1 += size;
T[t].u2 += size;
for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 += size;
T[node_num].father = t;
T[node_num].type = 1;
T[node_num].size = size;
T[t].children[filename] = node_num;
node_num++;
return "Y";
}
else {
node_num = oldnum;
Reback();
return "N";
}
}
}
string executeR() {
string filepath, temp, filename;
cin >> filepath;
int c = 0, l, t = 0; //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
for (l = filepath.size() - 1; filepath[l] != '/'; l--);
for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
while (c < l) {
temp = "";
for (++c; filepath[c] != '/'; c++) temp += filepath[c];
if (T[t].children.find(temp) == T[t].children.end()) {
return "Y"; //没有这个目录
}
else {
t = T[t].children[temp];
if (T[t].type)
return "Y";
}
}
if (T[t].children.find(filename) == T[t].children.end()) {
return "Y"; //没有这个文件
}
t = T[t].children[filename];
if (T[t].type) { //删除的是普通文件
int f = T[t].father;
T[f].children.erase(T[f].children.find(filename));
T[f].u1 -= T[t].size;
T[f].u2 -= T[t].size;
for (f = T[f].father; f != -1; f = T[f].father) T[f].u2 -= T[t].size;
return "Y";
}
else { //删除的是目录
int f = T[t].father;
T[f].children.erase(T[f].children.find(filename));
//我们称目录配额 LDd 是满足的,当且仅当 d 的孩子文件中,全部普通文件占用的存储空间之和不大于该配额值。
//T[f].u1 -= T[t].u2; //所以不用改动!!!
T[f].u2 -= T[t].u2;
for (f = T[f].father; f != -1; f = T[f].father) T[f].u2 -= T[t].u2;
return "Y";
}
}
string executeQ() {
string filepath, temp, filename="";
LL ld, lr;
cin >> filepath >> ld >> lr ;
if (ld == 0) ld = LLONG_MAX / 3; //若配额值为 0,则表示不对该项配额进行限制。
if (lr == 0) lr = LLONG_MAX / 3; //不限制的意思是恢复最大设定,而不是不改动当前设定!!!
int c = 0, l, t = 0; //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
for (l = filepath.size() - 1; filepath[l] != '/'; l--);
for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
while (c < l) {
temp = "";
for (++c; filepath[c] != '/'; c++) temp += filepath[c];
if (T[t].children.find(temp) == T[t].children.end()) {
return "N"; //没有这个目录
}
else {
t = T[t].children[temp];
if (T[t].type)
return "N";
}
}
if (!filename.size()) { //根目录
//注意其中任一配额导致系统不满足,都执行失败,不能修改目录文件的另一配额!!!
if (ld < T[t].u1 || lr < T[t].u2) {
return "N";
}
T[t].s1 = ld;
T[t].s2 = lr;
return "Y";
/*if (ld >= T[t].u1)
T[t].s1 = ld;
else
return "N";
if (lr >= T[t].u2)
T[t].s2 = lr;
else
return "N";
return "Y";*/
}
if (T[t].children.find(filename) == T[t].children.end()) {
return "N"; //没有这个文件
}
t = T[t].children[filename];
if (T[t].type) { //不是目录文件
return "N";
}
else { //设定目录文件的配额
if (ld < T[t].u1 || lr < T[t].u2) {
return "N";
}
T[t].s1 = ld;
T[t].s2 = lr;
return "Y";
}
}
int main()
{
int n;
scanf("%d", &n);
T[0].father = -1;
T[0].type = 0;
T[0].size = 0;
T[0].s1 = LLONG_MAX / 3;
T[0].s2 = LLONG_MAX / 3;
T[0].u1 = 0;
T[0].u2 = 0;
node_num = 1;
for (int i = 0; i < n; i++) {
char op;
scanf("%*c%c", &op);
if (op == 'C') {
cout << executeC() << endl;
}
else if (op == 'R') {
cout << executeR() << endl;
}
else if (op == 'Q') {
cout << executeQ() << endl;
}
}
return 0;
}
放一组可供测试的数据,题目给的有点弱,大家也可以自己生成(小声:我没试过😁)
17
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
C /A/B/1 1024
R /A/B
Q / 0 1
C /A/B/1 1
Q /A/B/1 0 0
Q / 0 2500000000
C /A/B/2 1000000000
C /A/B/3 900000000
C /A/B/4 1000000000
标准输出为:
Y
Y
N
N
Y
N
Y
Y
N
Y
Y
Y
N
Y
Y
Y
N
这里再放一篇可供参考的大佬代码
直接拷贝过来(大佬写得太好了,我们提前拷贝一下防止他删帖子我们没法学习了😝),方便大家对比本菜的代码,哈皮一下😆(侵删)
#include<iostream>
#include<cstdio>
#include<map>
#include<vector>
#include<cstring>
#pragma warning (disable:4996)
using namespace std;
int num = 0;
struct node {
int father; //父节点
map<string, int>child; //孩子节点
int type; //1:文件 2:目录
long long ld, lr;
long long size;
long long ld_r, lr_r;
}Node[4000010];
vector<pair<int, string> >reback;
void Reback()
{
int i;
for (i = 0; i < reback.size(); i++)
{
int a = reback[i].first;
string b = reback[i].second;
Node[a].child.erase(Node[a].child.find(b));
}
}
string executeC()
{
int i, j;
string filepath;
long long filesize;
cin >> filepath >> filesize; //输入普通文件路径以及普通文件大小
int last = -1;
for (i = filepath.length() - 1; i >= 0; i--)
{
if (filepath[i] == '/')
{
last = i; //找到最后一个/的位置,后面就是文件,前面就是目录
break;
}
}
int current = 1;
int id = 0;
reback.clear();
int oldnum = num;
//前面的目录操作
while (current < last)
{
string t = "";
while (current < last && filepath[current] != '/') //读入每一个目录文件的名字
{
t = t + filepath[current];
current++;
}
current++;
if (Node[id].child.find(t) == Node[id].child.end()) //没有这个目录,那就创建一个咯
{
num++;
Node[id].child[t] = num;
Node[num].father = id;
Node[num].type = 2;
Node[num].ld = LLONG_MAX / 3;
Node[num].lr = LLONG_MAX / 3;
reback.push_back(make_pair(id, t));
id = num;
}
else //存在这个目录文件
{
int childid = Node[id].child[t];
if (Node[childid].type == 1) //如果这个是文件,则直接输出'N'
{
num = oldnum;
Reback();
return "N";
}
id = childid;
}
}
//最后一个的文件操作
string t = "";
for (i = last + 1; i < filepath.length(); i++)
{
t = t + filepath[i];
}
if (Node[id].child.find(t) != Node[id].child.end()) //存在这个文件
{
int childid = Node[id].child[t];
if (Node[childid].type == 2) //如果这个是目录,直接输出'N'
{
num = oldnum;
Reback();
return "N";
}
}
//判断新的改变是否满足配额要求
long long changesize = 0;
if (Node[id].child.find(t) == Node[id].child.end()) changesize = filesize;
else changesize = -Node[Node[id].child[t]].size + filesize;
if (Node[id].ld_r + changesize > Node[id].ld)
{
num = oldnum;
Reback();
return "N";
}
int now = id;
while (now != -1)
{
if (Node[now].lr_r + changesize > Node[now].lr)
{
id = oldnum;
Reback();
return "N";
}
now = Node[now].father;
}
//如果满足配额要求,则执行创建普通文件操作以及修改变化的参数
if (Node[id].child.find(t) == Node[id].child.end())
{
num++;
Node[num].type = 1;
Node[num].father = id;
Node[num].size = filesize;
Node[id].child[t] = num;
}
else Node[Node[id].child[t]].size = filesize;
Node[id].ld_r = Node[id].ld_r + changesize;
now = id;
while (now != -1)
{
Node[now].lr_r = Node[now].lr_r + changesize;
now = Node[now].father;
}
return "Y";
}
string executeR()
{
int i;
string filepath;
cin >> filepath; //输入文件路径
int current = 1;
int id = 0;
int last = -1;
for (i = filepath.length() - 1; i >= 0; i--)
{
if (filepath[i] == '/')
{
last = i;
break;
}
}
while (current < last)
{
string t = "";
while (current < last && filepath[current] != '/')
{
t = t + filepath[current];
current++;
}
current++;
if (Node[id].child.find(t) == Node[id].child.end()) //没有这个目录,直接按照删除成功处理
{
return "Y";
}
else //存在这个目录,继续扫描下去
{
int childid = Node[id].child[t];
if (Node[childid].type == 1) return "Y";
id = childid;
}
}
string t = "";
for (i = last + 1; i < filepath.length(); i++) //提取最后想要删除的文件名
t = t + filepath[i];
if (Node[id].child.find(t) == Node[id].child.end())
return "Y";
long long delsize = 0;
int delNode = Node[id].child[t]; //删除文件的大小
if (Node[delNode].type == 1)
{
Node[id].ld_r = Node[id].ld_r - Node[delNode].size;
delsize = Node[delNode].size;
Node[id].child.erase(Node[id].child.find(t));
}
else if (Node[delNode].type == 2)
{
delsize = Node[delNode].lr_r;
Node[id].child.erase(Node[id].child.find(t));
}
int now = id;
while (now != -1)
{
Node[now].lr_r = Node[now].lr_r - delsize;
now = Node[now].father;
}
return "Y";
}
string executeQ()
{
int i;
string filepath;
cin >> filepath;
long long ld, lr;
cin >> ld >> lr;
if (ld == 0) ld = LLONG_MAX / 3;
if (lr == 0) lr = LLONG_MAX / 3;
int id = 0;
int last = -1;
int current = 1;
for (i = filepath.length() - 1; i >= 0; i--)
{
if (filepath[i] == '/')
{
last = i;
break;
}
}
while (current < last)
{
string t = "";
while (current < last && filepath[current] != '/')
{
t = t + filepath[current];
current++;
}
current++;
if (Node[id].child.find(t) == Node[id].child.end())
return "N";
else
{
int childid = Node[id].child[t];
if (Node[childid].type == 1) return "N";
id = childid;
}
}
string t = "";
for (i = last + 1; i < filepath.length(); i++) t = t + filepath[i];
int Qnode = 0;
if (t == "") Qnode = 0;
else
{
if (Node[id].child.find(t) == Node[id].child.end()) return "N";
else Qnode = Node[id].child[t];
}
if (Node[Qnode].type == 1) return "N";
if (ld < Node[Qnode].ld_r || lr < Node[Qnode].lr_r) return "N";
Node[Qnode].ld = ld;
Node[Qnode].lr = lr;
return "Y";
}
int main()
{
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
int i, j;
Node[0].type = 2;
Node[0].ld = LLONG_MAX / 3;
Node[0].lr = LLONG_MAX / 3;
Node[0].father = -1;
int op_num;
cin >> op_num; // 输入操作数
for (i = 1; i <= op_num; i++)
{
char op;
cin >> op; //输入操作类型
if (op == 'C')
{
cout << executeC() << endl; //执行创建普通文件操作
}
else if (op == 'R')
{
cout << executeR() << endl; //执行移除文件操作
}
else if (op == 'Q')
{
cout << executeQ() << endl; //执行设置配额值操作
}
}
return 0;
}