题目链接:带配额的文件系统
哭了又回到了原来的挫败感
最开始是数据存储错误,错以为当前文件的层次和文件名可以唯一定位到一个文件(想错了…),不过写了REMOVE和CREATE两个函数的大致逻辑。后面照着逻辑修改以后用路径来唯一定位一个文件,用set来区分是目录文件还是普通文件。
在acwing上通过了10 / 15 个数据最后超时没有AC
官网上是直接30分被判了错误QWQ,谁懂错误比超时还让人寒心。后面检查了一遍代码的大致逻辑感觉还是没有什么问题…
虽然分低但也是我写了N个小时的成果,我自贴一下吧,如果有能指出我错误的人我可真是太感谢了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, string> PIS;
bool Begin = true;
set<string> dfile;
set<string> nfile;
unordered_map<string, ll> nfile_size;
unordered_map<string, ll> dfile_child_quotas; //剩余的孩子配额, 用-1来标识没有限制
unordered_map<string, ll> dfile_future_child_quotas;
unordered_map<string, ll> dfile_child_weights;
unordered_map<string, ll> dfile_future_child_weights;
void CREATE()
{
if(Begin){ //初始化整个文件系统, 围绕根目录开展
dfile.insert("/");
dfile_child_quotas["/"] = -1;
dfile_future_child_quotas["/"] = -1;
dfile_child_weights["/"] = 0;
dfile_future_child_weights["/"] = 0;
Begin = false;
}
string dir;
ll file_size;
cin >> dir >> file_size;
//统计最深路径
int deepest_level = 1, j = 1;
for(j = 1;j < dir.size();j ++)
if(dir[j] == '/') deepest_level ++;
//后面为了字符串默认从第1级开始,所以这里额外补充一个跟根目录的比较
if(dfile_future_child_quotas["/"] != -1){
if(file_size > dfile_future_child_quotas["/"]){
cout << "N";
return;
}
}
if(deepest_level == 1){ //需要额外比较孩子目录
if(dfile_child_quotas["/"] != -1){
if(file_size > dfile_child_quotas["/"]){
cout << "N";
return;
}
}
}
j = 1;
int level = 1;
string now_dir = "";
vector<PIS> msg_for_update;
msg_for_update.push_back({0,"/"});
while(1){
if(level == deepest_level){
now_dir = dir;
if(dfile.count(now_dir)){ //如果有同名目录文件
//cout << "N, 创建文件与普通文件重名";
cout << "N";
return;
}
ll delta, pre_size = 0;
if(nfile.count(now_dir)){ //有同名普通文件,需要替换
//cout << "替换文件" << endl;
pre_size = nfile_size[now_dir];
}
else{
pre_size = 0;
nfile.insert(now_dir);
}
delta = file_size - pre_size; //50-20=30,也就是增大了 | 20-50 = -30
//更新配额信息
for(auto pis : msg_for_update){
//cout << endl << "此时在更新配额信息,需要更新的level和名字分别是" << endl;
//cout << pis.first <<" "<<pis.second << endl;
if(pis.first == deepest_level - 1){ //需要额外修改目录配额的信息,有没有可能受配置影响替换不成功呢
if(dfile_child_quotas[pis.second] != -1){
dfile_child_quotas[pis.second] -= delta;
}
dfile_child_weights[pis.second] += delta;
}
if(dfile_future_child_quotas[pis.second] != -1){
dfile_future_child_quotas[pis.second] -= delta;
}
dfile_future_child_weights[pis.second] += delta;
}
nfile_size[now_dir] = file_size;
cout << "Y";
return;
}
while(dir[j] != '/') j ++;
now_dir = dir.substr(0, j); //当前文件路径
if(nfile.count(now_dir)){ //如果当前路径下有同名的普通文件
cout << "N"; return;
//cout << "N, 路径中有目录文件与普通文件重名"; return;
}
if(dfile.count(now_dir)){
//当前已经有对应的目录,查看是否符合剩余配额
ll cquota = dfile_child_quotas[now_dir];
ll fcquota = dfile_future_child_quotas[now_dir];
if(level == deepest_level - 1){
//如果当前是倒数第二层目录,需要额外比较目录配额
if(file_size > cquota && cquota != -1){
cout << "N";
//cout << "N, 目录配额不满足";
return;
}
}
if(file_size > fcquota && fcquota != -1) { //不管那一层都要比较后代配额
cout << "N";
//cout << "N, 后代配额不满足";
return;
}
//此时符合条件,保存可能需要更新的配额信息
msg_for_update.push_back({level, now_dir});
}else { //此时必然合法
dfile.insert(now_dir);
dfile_child_quotas[now_dir] = -1;
dfile_future_child_quotas[now_dir] = -1;
dfile_child_weights[now_dir] = 0;
dfile_future_child_weights[now_dir] = 0;
msg_for_update.push_back({level, now_dir});
}
j ++;
level ++;
}
}
void REMOVE()
{
string dir;
cin >> dir;
//统计最深路径
int deepest_level = 1, j = 1;
for(j = 1;j < dir.size();j ++){
if(dir[j] == '/') deepest_level ++;
}
ll child_weights = 0, future_child_weights = 0;
if(nfile.count(dir)){ //如果是普通文件
child_weights = nfile_size[dir];
future_child_weights = nfile_size[dir];
nfile.erase(dir);
nfile_size.erase(dir);
}
else if(dfile.count(dir)){
child_weights = 0;
future_child_weights = dfile_future_child_weights[dir];
set<string> temp = dfile; //不用个set中转一下会在for循环里面被卡死?
//更新更为复杂, 对四个Map都要更新
for(auto df : temp){
if(df.find(dir) != string::npos){ //包含该路径子串
//cout << "正在删除目录文件:"<<df<<endl;
dfile.erase(df);
dfile_child_quotas.erase(df);
dfile_child_weights.erase(df);
dfile_future_child_quotas.erase(df);
dfile_future_child_weights.erase(df);
}
}
temp = nfile;
for(auto nf : temp){
if(nf.find(dir) != string::npos){
//cout << "正在删除普通文件:"<<nf<<endl;
nfile.erase(nf);
nfile_size.erase(nf);
}
}
}
else return; //当前路径不存在不进行任何操作
//对先行目录的数值进行更新
j = 1;
int level = 1;
string now_dir = "";
//对根目录更新
dfile_future_child_weights["/"] -= future_child_weights;
if(deepest_level == 1){
dfile_child_weights["/"] -= child_weights;
if(dfile_child_quotas["/"] != -1){
dfile_child_quotas["/"] += child_weights;
}
}
if(dfile_future_child_quotas["/"] != -1)
dfile_future_child_quotas["/"] += future_child_weights;
while(level < deepest_level){ //dir的完整目录已经在上面更新过了
while(dir[j] != '/') j ++;
now_dir = dir.substr(0, j);
//cout << "正在更新的路径:"<<now_dir<<endl;
dfile_future_child_weights[now_dir] -= future_child_weights;
if(level == deepest_level - 1){
dfile_child_weights[now_dir] -= child_weights;
if(dfile_child_quotas[now_dir] != -1){
dfile_child_quotas[now_dir] += child_weights;
}
}
if(dfile_future_child_quotas[now_dir] != -1)
dfile_future_child_quotas[now_dir] += future_child_weights;
j ++;
level ++;
}
}
void SETUP()
{
string dir;
ll child_quotas, future_child_quotas;
cin >> dir >> child_quotas >> future_child_quotas;
int deepest_level = 1, j = 1;
for(j = 1;j < dir.size();j ++)
if(dir[j] == '/') deepest_level ++;
int level = 1;
string now_dir = "";
j = 1;
while(level <= deepest_level){
if(level == deepest_level){ //查看是否能更新
now_dir = dir;
if(!dfile.count(now_dir)){ //如果路径所指文件不是目录文件
cout << "N";
return;
}
//此时当前路径上存在目录文件,查看是否能修改
if(child_quotas && child_quotas < dfile_child_weights[now_dir]){
cout << "N"; return;
}
if(future_child_quotas && future_child_quotas < dfile_future_child_weights[now_dir]){
cout << "N"; return;
}
//此时符合条件可以修改,修改两个剩余配额
ll delta;
if(child_quotas) delta = child_quotas - dfile_child_weights[now_dir];
else delta = -1;
dfile_child_quotas[now_dir] = delta;
if(future_child_quotas) delta = future_child_quotas - dfile_future_child_weights[now_dir];
else delta = -1;
dfile_future_child_quotas[now_dir] = delta;
cout << "Y";
return;
}
while(dir[j] != '/') j ++;
now_dir = dir.substr(0, j);
//需要一层层解析下来, 看路径(目录文件)是否存在
if(!dfile.count(now_dir)){
cout << "N";
return;
}
j ++;
level ++;
}
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 0;i < n;i ++){
char ch; cin >> ch;
if(ch == 'C') CREATE();
else if(ch == 'R') { REMOVE(); cout << "Y"; }
else if(ch == 'Q') SETUP();
cout << endl;
}
return 0;
}
未完待续…(分太低了明天找个满分答案看看,看能不能有啥新的收获并且找到自己的BUG。八百年没看长代码的真的惶恐QWQ)
看了一个满分代码参照着修改了代码,参考链接:带配额的文件系统
博主的写题思路其实和我最最最开始的构造相同,也就是用结构体和指针的方法来完成
三个功能函数的逻辑思维大体一致,这里采用一个child的map结构,有点类似于node = node->next
的感觉,由于对于每个节点只存储有当前节点的孩子文件,使得更新更为简单,搜索的时候类似于指针移动,一层层向下搜索
300ms | 100分
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 2e18;
struct File{
bool isFile; //是否为普通文件
ll size; //文件大小
ll ld, lr; //如果为目录文件,则其目录配额和孩子配额分别为多少
ll ldsize, lrsize; //孩子文件的总大小和后代文件的总大小(如果为目录)
unordered_map<string, File*> child; //存储当前目录下有哪些文件
//算是很久没有接触到的一个冒号语法(构造函数)
File(ll sz, bool file) : size(sz), isFile(file), lr(INF), ld(INF), ldsize(0), lrsize(0){}
};
vector<string> pathSplit;
//以'/'为分隔符切割字符串,提取内容(可以记忆的一个功能函数) | 注意下标从哪里开始!
vector<string> split(string path, string sp)
{
vector<string> ans;
for(ll i = 0, j = 0; i < path.size();i = j + 1){
j = path.find(sp, i);
if(j == -1){
j = path.size();
}
ans.push_back(path.substr(i, j - i));
}
return ans;
}
//找到当前路径的文件大小 | 目录文件则返回0 | 没有文件则返回0 | 有普通文件则返回普通文件的大小
ll fileSize(File *root)
{
for(ll i = 1;i < pathSplit.size() && root;i ++){
root = root->child.count(pathSplit[i]) ? root->child[pathSplit[i]] : nullptr;
}
if(!root) return 0; //如果没找到文件
if(!root->isFile) return 0;//如果找到的不是普通文件
return root->size;
}
bool judgePath(File *root, ll file_size)
{
for(ll i = 1;i < pathSplit.size() && root;i ++){
if(root->isFile) return false; //如果目录与普通文件重名
if(file_size + root->lrsize > root->lr) return false; //大于后代配额
if(i == pathSplit.size() - 1 && file_size + root->ldsize > root->ld) return false; //比较孩子配额, 注意root比i上一级
if(root->child.count(pathSplit[i])) root = root->child[pathSplit[i]]; //看pathSplit[i]是否为当前root节点的孩子节点
else root = nullptr;
}
return (!root || root->isFile);
// !root ---> 存在未创建的目录 | 文件(最后一级没有该文件), 此时必然为一个合法的路径
// root->isFile ----> 全部目录和文件已经创建, 当前路径指向一个普通文件(当前路径指向目录文件是非法的)
}
bool createFile(File *root, ll file_size)
{
ll nowSize = fileSize(root);
file_size -= nowSize; //为了方便对先前文件的孩子大小进行更新
if(!judgePath(root, file_size)) return false; //该路径是否为合法路径
for(ll i = 1;i < pathSplit.size() - 1;i ++){ //只遍历到目标普通文件的父文件夹
root->lrsize += file_size;
if(!root->child.count(pathSplit[i])){ //父目录下不存在该孩子目录, 创建一个目录文件
root->child[pathSplit[i]] = new File(0, false);
}
root = root->child[pathSplit[i]];
}
root->lrsize += file_size; root->ldsize += file_size; //此时需要更新孩子文件大小和后代文件大小
if(!root->child.count(pathSplit.back())){ //该普通文件不存在
root->child[pathSplit.back()] = new File(0, true);
}
root->child[pathSplit.back()]->size += file_size; //如果是替换,则加上差值 | 如果是非替换,则为输入的文件大小
return true;
}
void removeFile(File *root)
{
//先需要判断一下该路径是否合法
auto r = root;
for(ll i = 1;i < pathSplit.size() && r; i ++){
r = r->child.count(pathSplit[i]) ? r->child[pathSplit[i]] : nullptr;
}
if(!r) return; //有任何一级目录错误
ll size = r->isFile ? r->size : r->lrsize; //这里记录需要修改的文件大小. 普通文件: 需要修改的大小为文件本身 | 目录文件: 需要修改的大小为其后代文件的大小
for(ll i = 1;i < pathSplit.size() - 1;i ++){ //注意这里只遍历到倒数第二层, 也即目标文件的父文件夹
root->lrsize -= size; //修改后代文件的大小
root = root->child[pathSplit[i]];
}
root->lrsize -= size;
if(r->isFile) root->ldsize -= size; //如果是普通文件,则需要额外对孩子文件的大小进行修改
root->child.erase(pathSplit.back()); //由于是递归的一层一层向下搜索,所以这里只需要修改一层即可
}
bool QFile(File *root, ll ld, ll lr)
{
//先找到对应的文件
for(ll i = 1; i < pathSplit.size() && root;i ++){
root = root->child.count(pathSplit[i]) ? root->child[pathSplit[i]] : nullptr;
}
if(!root || root->isFile) return false; //如果路径错误或为普通文件
if(root->ldsize > ld || root->lrsize > lr) return false; //如果已有的文件大于配额
root->ld = ld;
root->lr = lr;
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
ll n; cin >> n;
string ch, path;
File *root = new File(0, false); //初始化根目录
while(n --){
cin >> ch >> path;
pathSplit = split(path, "/");
if(ch == "C"){
ll size; cin >> size;
if(createFile(root, size)) cout << "Y\n";
else cout << "N\n";
}else if(ch == "R"){
removeFile(root);
cout << "Y\n";
}else{
ll ld, lr;
cin >> ld >> lr;
ld = ld == 0 ? INF : ld;
lr = lr == 0 ? INF : lr;
if(QFile(root, ld, lr)) cout << "Y\n";
else cout << "N\n";
}
pathSplit.clear();
}
return 0;
}