CSP 2020-12
3 带配额的文件系统
tag:复杂模拟 结构体
题面链接
http://118.190.20.162/view.page?gpid=T121.
题意
模拟一个文件系统,和我们所用的文件系统类似,是树形结构;不同之处在于,可能会对文件夹有容量限制。对于给定的三种操作:C(存储文件),R(删除文件),Q(设定容量限制),输出操作结果(Y/N)。
思路
利用结构体进行模拟,每个结点包括:名称(name)、子节点(child)、父节点(parent)、孩子文件配额(ld)、后代文件配额(lr)、孩子文件大小(sizec)、后代文件大小(sizeac)、文件大小(size)、文件类型(type)等属性。
对于三种操作分别进行模拟,并给出结果。整个程序的思路导图如下图所示。
需要特别注意的一点是:当C操作失败时,需要删除新创建的目录文件,否则下次创建同名普通文件时会出错。
附样例的文件系统图:
样例1:
样例2:
源码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
string name="root";//名称
vector<struct node*> child;//孩子节点指针
struct node* parent=NULL;//父节点指针
ll ld=0,lr=0,sizec=0,sizeac=0,size=0;
// ld:孩子文件配额,lr:后代文件配额,sizec:孩子文件大小,sizeac:后代文件大小,size:普通文件大小
bool type=false;//文件类型,false:目录文件;true:普通文件。
};
node root;//根节点
//判断p结点及所有祖先结点,后代文件配额是否满足
bool panduan(node *p,ll bianhua){
if(p==NULL) return true;
if((p->lr==0 || p->sizeac+bianhua<=p->lr) && panduan(p->parent,bianhua)){//当前结点后代文件配额满足,且所有祖先结点后代文件配额均满足
p->sizeac+=bianhua;return true;
}
else return false;
}
vector<string> v;//保存路径
//C操作
// C /A/B/1 1024
void opec(string s,ll size){
string dir="";
//处理字符串,不必考虑v为空的情况
v.clear();//清空v
//获取路径
for(ll i=1;i<s.size();i++){
if(s[i]=='/'){v.push_back(dir);dir="";continue;}
dir+=s[i];
}
v.push_back(dir);
node *p=&root;//当前结点指针
ll len;//长度
bool isp;//标记
bool flag=true;//标记目录是否是第一个被创建的
node *huisu;
ll ii;
//处理前面目录
for(ll i=0;i<v.size()-1;i++){
len=p->child.size();
isp=true;
for(ll j=0;j<len;j++){
//找到该目录
if(p->child[j]->name==v[i]){
//该目录为普通文件,此次操作失败
if(p->child[j]->type){cout<<"N\n";return;}
//该目录已存在,进行下层目录查找
else{
p=p->child[j];//修改当前查找结点的指针
isp=false;break;
}
}
}
//没有找到该目录,创建新目录
if(isp){
node *n;
n=new node;
n->name=v[i];
n->parent=p;
//记录新创建目录的起点,以便于删除
if(flag){
flag= false;
huisu=p;
ii=len;
}
p->child.push_back(n);
p=n;//修改当前查找结点的指针
}
}
//处理最后的普通文件
len=p->child.size();
//判断该文件是否已存在
for(ll i=0;i<len;i++){
//该文件已存在
if(p->child[i]->name==v[v.size()-1]){
//该文件为目录文件,操作失败
if(p->child[i]->type== false) {cout<<"N\n";return;}
//该文件为普通文件
ll bianhua=p->child[i]->size-size;//变化值:旧文件-新文件
//大小未变化,直接返回
if(bianhua==0){cout<<"Y\n";return;}
//新文件大小<旧文件,直接更新
else if(bianhua>0){
p->child[i]->size=size;
p->sizec-=bianhua;//更新父节点的孩子文件大小
//更新所有祖先节点的后代文件大小
while(p!=NULL){
p->sizeac-=bianhua;
p=p->parent;
}
cout<<"Y\n";return;
}
//新文件大小>旧文件,判断是否超过配额
else{
bianhua*=-1;
//满足配额
if((p->ld==0 || p->sizec+bianhua<=p->ld) && panduan(p,bianhua)){//孩子文件配额,后代文件配额均满足时
p->child[i]->size=size;//更新文件大小
p->sizec+=bianhua;//更新父节点,孩子文件大小
cout<<"Y\n";return;
}
//不满足
else{cout<<"N\n";return;}
}
}
}
//该文件不存在,创建新文件
//满足配额
if((p->ld==0 || p->sizec+size<=p->ld) && panduan(p,size)){//孩子文件配额,后代文件配额均满足时
p->sizec+=size;
node *n;
n=new node;
n->name=v[v.size()-1];
n->size=size;
n->type= true;//普通文件
n->parent=p;
p->child.push_back(n);
cout<<"Y\n";return;
}
//不满足
else{
//删除已创建目录***********************************
if(flag==false){huisu->child.erase(huisu->child.begin()+ii);}
cout<<"N\n";return;
}
}
//R操作
// R /A/B
void oper(string s){
string dir="";
//处理字符串,不必考虑v为空的情况
v.clear();//清空v
//获取路径
for(ll i=1;i<s.size();i++){
if(s[i]=='/'){v.push_back(dir);dir="";continue;}
dir+=s[i];
}
v.push_back(dir);
node *p=&root;//当前结点指针
ll len;//长度
bool isp;//标记
//处理前面目录
for(ll i=0;i<v.size()-1;i++){
len=p->child.size();
isp=true;
for(ll j=0;j<len;j++){
//找到该目录
if(p->child[j]->name==v[i]){
//该目录为普通文件,返回
if(p->child[j]->type){cout<<"Y\n";return;}
//该目录存在,进行下层目录查找
else{
p=p->child[j];//修改当前查找结点的指针
isp=false;break;
}
}
}
//没有找到该目录,返回
if(isp){cout<<"Y\n";return;}
}
//处理最后的普通文件 or 目录文件
len=p->child.size();
//判断该文件是否存在
for(int i=0;i<len;i++){
//该文件存在
if(p->child[i]->name==v[v.size()-1]){
//该文件为目录
if(p->child[i]->type== false){
ll size=p->child[i]->sizeac;//目录的所有后代文件大小
p->child.erase(p->child.begin()+i);//删除此节点
//所有祖先结点的后代文件大小减去size
while(p!=NULL){
p->sizeac-=size;
p=p->parent;
}
cout<<"Y\n";return;
}
//该文件为普通文件
else{
ll size=p->child[i]->size;
p->child.erase(p->child.begin()+i);//删除此节点
p->sizec-=size;//父结点的孩子文件大小减去size
//所有祖先结点的后代文件大小减去size
while(p!=NULL){
p->sizeac-=size;
p=p->parent;
}
cout<<"Y\n";return;
}
}
}
//该文件不存在,返回
cout<<"Y\n";return;
}
//Q操作
//Q / 0 1
void opeq(string s,ll ld,ll lr){
string dir="";
//处理字符串,需要考虑v为空的情况
v.clear();//清空v
//获取路径
for(ll i=1;i<s.size();i++){
if(s[i]=='/'){v.push_back(dir);dir="";continue;}
dir+=s[i];
}
v.push_back(dir);
node* p=&root;//当前结点指针
if(v[0]==""){//对根节点的操作
//满足配额
if((ld==0 || p->sizec<=ld) && (lr==0 || p->sizeac<=lr)){
p->ld=ld;p->lr=lr;
cout<<"Y\n";return;
}
//不满足
else{cout<<"N\n";return;}
}
int len;//长度
bool isp;//标记
//处理目录
for(ll i=0;i<v.size();i++){
len=p->child.size();
isp=true;
for(ll j=0;j<len;j++){
//找到该目录
if(p->child[j]->name==v[i]){
//该目录为普通文件,此次操作失败
if(p->child[j]->type){cout<<"N\n";return;}
//该目录已存在,进行下层目录查找
else{
p=p->child[j];//修改当前查找结点的指针
//最后一个文件时,进行配额判断
if(i==v.size()-1){
//满足配额
if((ld==0 || p->sizec<=ld) && (lr==0 || p->sizeac<=lr)){
p->ld=ld;p->lr=lr;
cout<<"Y\n";return;
}
//不满足
else{cout<<"N\n";return;}
}
isp=false;break;
}
}
}
//没有找到该目录,此次操作失败
if(isp){cout<<"N\n";return;}
}
}
int main() {
ll n;
string s;
ll size1,size2;
char c;
cin>>n;
while(n--){
cin>>c;
switch(c){
case 'C':
cin>>s>>size1;
opec(s,size1);
break;
case 'R':
cin>>s;
oper(s);
break;
case 'Q':
cin>>s>>size1>>size2;
opeq(s,size1,size2);
break;
}
}
return 0;
}
/*
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
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
10
C /A/B 1
Q /A 2 2
C /A/C/D 1024
C /A/C 1
Q /A 0 0
C /A/C 1
*/