题目
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
Input
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
Ouput
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
Sample Input
1
22
MKDIR dira
CD dirb
CD dira
MKDIR a
MKDIR b
MKDIR c
CD ..
MKDIR dirb
CD dirb
MKDIR x
CD ..
MKDIR dirc
CD dirc
MKDIR y
CD ..
SZ
LS
TREE
RM dira
TREE
UNDO
TREE
Sample Ouput
OK
ERR
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
9
dira
dirb
dirc
root
dira
a
b
c
dirb
x
dirc
y
OK
root
dirb
x
dirc
y
OK
root
dira
a
b
c
dirb
x
dirc
y
思路
因为在每个子目录下,使用字典序排序,因此可以使用map记录子目录。map的first存子目录的名字,second存目录类型的指针。
目录结构体成员如下:
struct Directory{
string name;
map<string, Directory*> children;
Directory* parent;
int subtreeSize;
vector<string>* tenDescendants;
bool updated;
};
声明数据类型command,对输入的指令进行操作,成员包括指令类型、指令参数、目录。
struct command{
const string COMMAND_NAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
int type;
string arg;
Directory* tmpDir;
command(string str){
this->type = -1;
for (int i = 0; i < 7; i++){
if (COMMAND_NAMES[i] == str){
this->type = i;
if (i < 3){
cin >> tmpstr;
this->arg = tmpstr;
}
return;
}
}
}
};
目录中还包含了题目要求的操作。其中,前三个操作并不是直接输出,而转移到了处理函数solve()中解决。因为这三个操作可能需要UNDO,因此不仅需要输出,还要返回UNDO必要的参数。
mkdir:则是在当前目录下创建一个新目录,所以我们需要一个指针now来指向当前目录,初始值为root。在创建成功之后,返回刚刚创建的目录的指针,如果指针不为空,则创建成功,将刚刚执行成功的这条指令加入到一个vector对象中, 该对象存储已经成功执行的命令。
rm:与mikdir类似。
cd:进入一个新目录,在操作成功后,更改now指针的指向。
sz,ls,tree:直接调用函数,不需要其他改变。
undo:则从vector中取出一条最近执行成功的指令,如果为mkdir,则将新建的目录删除,如果为rm,则将之前删除的目录加上,如果为cd,则返回上一级。
本题,最最关键的问题是时间限制和数据范围。命令总数1e5,也就是说TREE操作的数目极限情况下差不多要到1e5次,多组数据最多20组,显然如果每一次TREE操作都进行遍历的话会超时。
因此,采用懒更新的方法,在每次进行插入或删除操作时标记更新,然后在进行TREE操作时将答案存储。如果下次进行TREE操作没有更新,无需重新遍历,直接输出即可。
代码
#include<iostream>
#include<map>
#include<vector>
using namespace std;
string tmpstr;
struct Directory{
string name;
map<string, Directory*> children;
Directory* parent;
int subtreeSize;
vector<string>* tenDescendants;
bool updated;
Directory(){
tenDescendants= new vector<string>;
this->updated = true;
parent = new Directory();
}
Directory(string name, Directory* parent){
this->name = name;
this->parent = parent;
this->subtreeSize = 1;
tenDescendants= new vector<string>;
}
bool addChild(Directory* ch){
if (children.find(ch->name) != children.end())
return false;
children[ch->name] = ch;
maintain(+ch->subtreeSize);
return true;
}
Directory* getChild(string name){
auto it = children.find(name);
if (it == children.end())
return NULL;
return it->second;
}
Directory* mkdir(string name){
if (children.find(name) != children.end())
return NULL;
Directory* ch = new Directory(name, this);
children[name] = ch;
maintain(+1);
return ch;
}
Directory* rm(string name){
auto it = children.find(name);
if (it == children.end())
return NULL;
maintain(-1 * it->second->subtreeSize);
children.erase(it);
return it->second;
}
Directory* cd(string name){
if (".." == name)
return this->parent;
return getChild(name);
}
void maintain(int delta){
updated = true;
subtreeSize += delta;
if (parent != NULL)
parent->maintain(delta);
}
void sz(){
cout << this->subtreeSize << endl;
}
void ls(){
int sz = children.size();
if (sz == 0)
cout << "EMPTY" << endl;
else if (sz <= 10)
for (auto& entry : children)
cout << entry.first << endl;
else{
auto it = children.begin();
for (int i = 0; i < 5; i++, it++)
cout << it->first << endl;
cout << "..." << endl;
it = children.end();
for (int i = 0; i < 5; i++)
it--;
for (int i = 0; i < 5; i++, it++)
cout << it->first << endl;
}
}
void tree(){
if (subtreeSize == 1)
cout << "EMPTY" << endl;
else if (subtreeSize <= 10){
if (this->updated){
tenDescendants->clear();
treeAll(tenDescendants);
this->updated = false;
}
for (int i = 0; i < subtreeSize; i++)
cout << tenDescendants->at(i) << endl;
}
else{
if (this->updated){
tenDescendants->clear();
treeFirst(5, tenDescendants);//前五个
treeLast(5, tenDescendants);//后五个
this->updated = false;
}
for (int i = 0; i < 5; i++)
cout << tenDescendants->at(i).c_str() << endl;
cout << "..." << endl;
for (int i = 9; i >= 5; i--)
cout << tenDescendants->at(i).c_str() << endl;
}
}
public:
void treeAll(vector<string>* v){
v->push_back(name);
for (auto& entry : children)
entry.second->treeAll(v);
}
void treeFirst(int num, vector<string>* v){
v->push_back(name);
if (--num == 0)
return;
int n = children.size();
auto it = children.begin();
while (n--){
int sts = it->second->subtreeSize;
if (sts >= num){
it->second->treeFirst(num, v);
return;
}
else{
it->second->treeFirst(sts, v);
num -= sts;
}
it++;
}
}
void treeLast(int num, vector<string>* v){
int n = children.size();
auto it = children.end();
while (n--){
it--;
int sts = it->second->subtreeSize;
if (sts >= num){
it->second->treeLast(num, v);
return;
}
else{
it->second->treeLast(sts, v);
num -= sts;
}
}
v->push_back(name);
}
};
struct command{
const string COMMAND_NAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
int type;
string arg;
Directory* tmpDir;
command(string str){
this->type = -1;
for (int i = 0; i < 7; i++){
if (COMMAND_NAMES[i] == str){
this->type = i;
if (i < 3){
cin >> tmpstr;
this->arg = tmpstr;
}
return;
}
}
}
};
void solve()
{
int n;
cin >> n;
Directory* now = new Directory("root", NULL);
vector<command*> cmdList;
while (n--){
cin >> tmpstr;
command* cmd = new command(tmpstr);
switch (cmd->type){
case 0:{
cmd->tmpDir = now->mkdir(cmd->arg);//MKDIR
if (cmd->tmpDir == NULL)
cout << "ERR" << endl;
else{
cout << "OK" << endl;
cmdList.push_back(cmd);
}
break;
}
case 1:{
cmd->tmpDir = now->rm(cmd->arg);//MKDIR
if (cmd->tmpDir == NULL)
cout << "ERR" << endl;
else{
cout << "OK" << endl;
cmdList.push_back(cmd);
}
break;
}
case 2:{
Directory* ch = now->cd(cmd->arg);
if (ch == NULL)
cout << "ERR" << endl;
else{
cout << "OK" << endl;
cmd->tmpDir = now;
now = ch;
cmdList.push_back(cmd);
}
break;
}
case 3:
now->sz();
break;
case 4:
now->ls();
break;
case 5:
now->tree();
break;
case 6:{
bool success = false;
while (!success && !cmdList.empty()){
cmd = cmdList.back();
cmdList.pop_back();
switch (cmd->type){
case 0:
success = now->rm(cmd->arg) != NULL;
break;
case 1:
success = now->addChild(cmd->tmpDir);
break;
case 2:
now = cmd->tmpDir; success = true;
break;
default:
break;
}
}
cout << (success ? "OK" : "ERR") << endl;
break;
}
default:
break;
}
}
}
int main(){
int T;
cin >> T;
while (T--)
solve();
return 0;
}