验收保命
作者:互联网
第九周:
化学方程式
unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。哈希表详细介绍
unordered_map<string, int> a;//
创建容器存储1元素名的2原子个数
//按=将方程式分割成两部分
分解出加号两侧的化学式的原子数目
通过递归对化学式进行原子数目的计算
其中需要区分,一个字母的元素,两个字母的元素和里面带括号的化学式。只要后面有数字就要递归。
分别计算完这两部分的各原子数目后判等
#include <bits/stdc++.h>
using namespace std;
unordered_map<string, int> a;//1元素名,2原子个数
int n;
string s;
//返回s的[first,last]区间对应的数字,函数返回之后传递给first的实参将移动到第一个非数字字符的位置
int d(int& first, int last) {
int i;
for (i = 0; first <= last and isdigit(s[first]); first++)
i = i * 10 + s[first] - '0';//获取系数
if(i==0)
return 1;
else return i;
}
void f(int first, int last, int e) { //计算s的[first,last]区间的原子及其对应系数,e是后面的系数
if (first == last || (last - first == 1 && islower(s[last]))) { //化学式是单个原子,只有一个字母或者第二个字母是小写字母
a[s.substr(first, last - first + 1)] += e; //该原子个数递增e
return;
}
e *= d(first, last); //该化学式内所有原子基本系数要乘上该化学式起始系数
for (int i = first, j = i + 1; i <= last; i = j,j++) { //遍历化学式
if (isupper(s[i])) { //是原子
if (j <= last && islower(s[j]))
j++;
int k = j;
f(i, k - 1, e * d(j, last)); //递归处理
} else if (s[i] == '(') { //遇到(
for (int num = 1; num != 0; j++) { //i是(的位置最后j是)的位置
if (s[j] == '(')
num++;
else if (s[j] == ')')
num--;
}
int k=j;
f(i + 1, k - 1, e * d(j, last)); //递归处理
}
}
}
void expression(int first, int last, int e) { //按i+分离出所有化学式
for (int i = first, j = i; i <= last;) {
j = s.find('+', i);//从i开始搜索‘+’
if (j == string::npos || j > last)//string:npos是个特殊值,说明查找没有匹配
j = last + 1;//最后一个无加号的项
f(i, j - 1, e);//把每个项分解出来计算原子数目//f函数中的last是加号前的那个字符
i = j + 1;
f(i, j - 1, e);//把每个项分解出来计算原子数目
i = j + 1;
}
}
int main() {
cin >> n;
while (n--) {
cin >> s;
a.clear();
int k = s.find('='); //按=将方程式分割成两部分
expression(0, k - 1, 1);
expression(k + 1, s.size() - 1, -1);
//查找有无原子个数不为0
auto i = find_if(a.begin(), a.end(), [](const pair<string, int>& p) { return p.second != 0; });
if(i == a.end()) cout <<"Y\n";else cout<<"N\n";
}
}
B : 带配额的文件系统
路径测试
⚫对于输入的路径,我们应当首先检查其合法性,其状态有:
⚫路径错误:路径把普通文件当做目录文件进行访问
⚫路径不完整:执行到某一目录,发现下一级目录不存在
⚫成功找到指定的文件。
⚫路径测试需要向之后的其他操作提供输入路径的状态,从而进行不同的处理
⚫改变配额(路径测试状态要求:找到对应文件且是目录文件)
⚫统计目录中的文件是否满足输入的配额:满足则更改,否则操作失败。
⚫由于子树大小可能是O(操作数) 的,若统计需要枚举子树中的节点,那统计操作的时间复杂度即为O(操作数)
⚫若使用上述方法,总时间复杂度为O(操作数^2),这是无法接受的。
⚫如果子树中节点的大小之和是在插入删除时进行维护的(即作为目录节点的一个属性,保存在目录节点中),那么每次只需O(1) 进行判断即可。
⚫如此,就需要在插入或删除文件是维护节点的:当前子树节中所有文件大小之和、当前子节点的文件大小之和
⚫移除文件(路径测试状态要求:找到对应文件)
⚫获取文件大小
⚫普通文件:直接使用文件大小
⚫目录文件:以当前目录文件为根的子树内所有文件大小之和(已维护)
⚫更新从根到当前文件的所有目录文件的当前配额使用信息
⚫删除对应文件
创建普通文件
⚫新建文件:(路径测试状态要求:路径不完整)
⚫测试加入后是否满足配额的要求
⚫从根到当前文件路径上的所有目录文件都需要满足子树配额
⚫当前文件的父节点还需额外满足后代配额
⚫可以使用预分配方法(假设分配上去,判断是否合法)
⚫若满足配额要求,则更新从根到当前文件的所有目录文件的当前配额使用信息。从根向下依次访问时,若发现下一层目录不存在,则新建目录文件后再进行访问。
⚫替换文件:(路径测试状态要求:找到对应文件且是普通文件)
⚫为了简化关于配额的操作,先删除原有文件,然后判断当前文件能否成功插入:若可行则保留;若不可行则重新新建原有文件。
输入操作类类型与路径,使用find()函数获取路径
1、若操作类型是创建普通文件
若路径存在,检查该路径是否与目录文件冲突,冲突则创建失败
否则是普通文件,先删除原有文件,然后判断当前文件能否成功插入:若可行则保留;若不可行则重新新建原有文件。
若路径不完整,则新建文件,测试加入后是否满足配额的要求,假设分配上去,判断是否合法,若满足配额要求,则更新从根到当前文件的所有目录文件的当前配额使用信息。从根向下依次访问时,若发现下一层目录不存在,则新建目录文件后再进行访问。
若路径错误则创建失败
2、移除操作
若该路径所指的文件不存在,则不进行任何操作
若成功找到对应文件,
获取该文件大小
若是普通文件:直接使用文件大小,删除对应文件
若是目录文件:以当前目录文件为根的子树内所有文件大小之和(已维护)
则移除该目录及其所有后代文件。被移除的目录上设置的配额值也被移除。
该指令始终认为能执行成功。
3、设置配额值
设置配额值的目录的路径、目录配额和后代配额。
若路径所指的目录不存在,或者不是目录文件,则该指令执行不成功。
若在该目录上已经设置了配额,则将原配额值替换为指定的配额值。
检查在应用新的配额值后,该文件系统配额是否满足,否则该指令执行不成功。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define N 5000005
struct file
{int type;//文件类型,0普通文件1目录
ll size,LD,LR,sLD,sLR;
//普通文件的大小,目录配额(子树),后代配额,直属孩子节点中普通文件的大小之和(现有),后代节点中普通文件的大小之和(现有)
map<string,int>child;//目录内信息
file(int _type=0,ll _size=0){
type=_type,size=_size;LD=LR=sLD=sLR=0;
child.clear();
}
bool check_addsize(ll sz,bool last=false){//文件大小分配预测试
if(LR&&sLR+sz>LR)return false;
if(last&&LD&&sLD+sz>LD)return false;
return true;
}
void addsize(ll sz,bool last=false)//文件大小分配
{
if(last)sLD+=sz;sLR+=sz;//逗号改成分号万事大吉
}
bool change_size(ll new_LD,ll new_LR){
if(new_LR&&sLR>new_LR)return false;
if(new_LD&&sLD>new_LD)return false;
LR=new_LR;
LD=new_LD;
return true;
}
};
struct filesystem
{
int root,cnt;//根目录节点,计数器
vector<string>path;//目录路径信息
string name;//文件名
file files[N];//
filesystem(){
root=1;
cnt=1;
files[1]=file(1);
}
void get_path(string s)//路径拆分
{
path.clear();
name="";
string ch;
stringstream ss(s);
getline(ss,ch,'/');
while( getline(ss,ch,'/')) path.push_back(ch);
if(path.size())name=ch,path.pop_back();
}
int find(){//路径测试,返回节点指针与查找状态
int now=root;
if(name=="")return root;
for(auto &x:path){
int t=files[now].child[x];//儿子的下标
if(!t)return 0;//目录缺失
if(files[t].type==0)return -1;//目录文件与普通文件冲突,即非叶子普通文件
now=t;
}
return files[now].child[name];
}
int get_type(int now){//返回当前文件以及下级目录的大小总和
return files[now].type;
}
ll get_size(int now){
if(files[now].type==0)return files[now].size;//是普通文件
return files[now].sLR;//不是普通文件返回后代节点中普通文件的大小之和
}
bool create_file(ll size){//新建文件,判断是否满足配额,维护大小数据
int now=root;
for(auto &x:path){//是否可以创建文件
if(!files[now].check_addsize(size))return false;//配额(空间)不够
if(files[now].child[x]==0){//目录缺失
files[++cnt]=file(1);
files[now].child[x]=cnt;
}
now=files[now].child[x];
}
if(!files[now].check_addsize(size,true))return false;
now=root;
for(auto &x:path){
files[now].addsize(size);
now=files[now].child[x];
}
files[now].addsize(size,true);
files[++cnt]=file(0,size);//创建文件
files[now].child[name]=cnt;
return true;
}
bool remove_file(ll size){//删除文件,维护大小数据
int now=root;
for(auto &x:path){
files[now].addsize(-size);
now=files[now].child[x];
}
if(files[files[now].child[name]].type==0)files[now].addsize(-size,true);
else files[now].addsize(-size);
files[now].child.erase(name);
}
bool change_size(int now,ll new_LD,ll new_LR){
return files[now].change_size(new_LD,new_LR);}
}fs;
int main()
{
int n;
scanf("%d",&n);
while(n--){
string s,pa;
cin>>s>>pa;
fs.get_path(pa);//获取路径
int now =fs.find();//现在是第几个目录
if(s=="C"){
ll size;
cin>>size;//文件大小
if(now==-1){//目录文件与普通文件冲突
cout<<"N"<<endl;
continue;
}
else if(now>0){
if(fs.get_type(now)==0)fs.remove_file(fs.get_size(now));//路径所指的文件已经存在,且也是普通文件的,则替换这个文件,先删
else{ cout<<"N"<<endl;
continue;
}
}
if(fs.create_file(size))cout<<"Y"<<endl;//创建新文件成功
else{
if(now>0)fs.create_file(fs.get_size(now));
cout<<"N"<<endl;//若要创建的目录文件与已有的同一双亲目录下的孩子文件中的普通文件名称重复,则该指令不能执行成功。
}
}
if(s=="R"){ if(now>0)fs.remove_file(fs.get_size(now));//移除操作
cout<<"Y"<<endl;}
if(s=="Q"){ll new_LD,new_LR;//更改配额
cin>>new_LD>>new_LR;
// cout<<"some"<<now<<" "<<fs.get_type(now)<<endl;
if(now>0&&fs.get_type(now)==1){
if(fs.change_size(now,new_LD,new_LR))cout<<"Y"<<endl;
else cout<<"N"<<endl;}
else {cout<<"N"<<endl;continue;}
}
}
}
上次的约球模测
//定义两个二维数组,第一维用来表示人,第二维表示每人任务的开始时间/结束时间
//数组d用来存储所有时间段的端点,将所有的起始点都存入d并对d进行排序
在这些所有的时间点中寻找第一个满足条件的左端点和第一个不满足条件的右端点,则中间的点都是满足条件的
(条件包括)
//1、如果某人的任务数为0,当前时间段有空的人数加1
//2、如果当前时间段小于某人的最早任务的开始时间或者晚于最后一个任务的结束时间,则当前时间段有空的人数加1.
//3、对于某人的所有任务,如果该时间段在上一个任务结束之后并且在下一个任务开始之前,则当前时间段有空的人数加1.
//如果当前时间段有时间的人数大于等于2,并且大于等于总人数减1,则该区间可能有效。最后再判断该区间的时间是否大于等于1小时,如果成立,则输出该区间,否则,去找下一个区间。
#include<bits/stdc++.h>
using namespace std;
int T;
struct Date {//结构体,存储时间
int year, month, day, hour, minute, sec;
Date(){}
Date(int y, int m, int d, int h, int mi, int se)
{
year = y,month = m,day = d,hour = h,minute = mi,sec = se;
}
bool operator != (const Date d)const
{
return (year != d.year) || (month != d.month) || (day != d.day) || (hour != d.hour) || (minute != d.minute) || (sec != d.sec);
}
bool operator == (const Date d)const
{
return (year == d.year) && (month == d.month) && (day == d.day) && (hour == d.hour) && (minute == d.minute) && (sec == d.sec);
}
bool operator >= (const Date d)const
{
return !(*this < d);
}
bool operator <= (const Date d)const
{
return !(d < *this);
}
Date& operator = (Date d)
{
year = d.year;
month = d.month;
day = d.day;
hour = d.hour;
minute = d.minute;
sec = d.sec;
return *this;
}
bool operator < (const Date d)const//自定义<
{
if (year != d.year) return year < d.year;
else if (month != d.month) return month < d.month;
else if (day != d.day) return day < d.day;
else if (hour != d.hour) return hour < d.hour;
else if (minute != d.minute) return minute < d.minute;
else return sec < d.sec;
}
}s[30][110],e[30][110],d[5000];
//s第一维用来表示人,第二维表示每人任务的开始时间
//e第一维表示人,第二维表示每个任务的结束时间
//d用来存储所有时间段的端点
int num[30],ffree;
//num用来表示每人的任务数,
bool search(int a,int b)
{//判断一个时间点是否符合标准
ffree=0;
for(int i=0;i<b;i++)
{//枚举每个人,若干判断标准
if(num[i]==0)1、如果某人的任务数为0,当前时间段有空的人数加1
{
ffree++;
continue;
}
if(d[a]<s[i][0])//2、如果当前时间段早于某人的最早任务的开始时间,则当前时间段有空的人数加1.
{
ffree++;
continue;
}
if(e[i][num[i]-1]<=d[a])//晚于最后一个任务的结束时间则当前时间段有空的人数加1.
{
ffree++;
continue;
}
for(int j=0;j<num[i];j++)//对于某人的所有任务,如果该时间段在上一个任务结束之后并且在下一个任务开始之前,则当前时间段有空的人数加1.
{
if(j+1<num[i])
if((d[a]<s[i][j+1])&&(d[a]>=e[i][j]))
{
ffree++;
continue;
}
}
}
if(ffree>=2&&ffree>=b-1&&ffree<=b)//如果当前时间段有时间的人数大于等于2,并且大于等于总人数减1,则该区间可能有效。最后再判断该区间
return true;
else
return false;
}
bool onehour(Date d1,Date d2)
{//判断时间是否大于等于1小时
d1.hour++;//将起始的小时数加1,若等于24,进行相应处理
if (d1.hour == 24)
{
d1.hour = 0;
d1.day++;
if (d1.day == 31)
{
d1.day = 1;
d1.month++;
if (d1.month == 13)
{
d1.month = 1;
d1.year++;
}
}
}
if (d1 <= d2)
return true;
else
return false;
}
void print(int l,int r)
{
cout << "appointment possible from ";
if (d[l].month < 10)
cout << "0";
cout << d[l].month << "/";
if (d[l].day < 10)
cout << "0";
cout << d[l].day << "/" << d[l].year << " ";
if (d[l].hour < 10)
cout << "0";
cout << d[l].hour << ":";
if (d[l].minute < 10)
cout << "0";
cout << d[l].minute << ":";
if (d[l].sec < 10)
cout << "0";
cout << d[l].sec << " to ";
if (d[r].month < 10)
cout << "0";
cout << d[r].month << "/";
if (d[r].day < 10)
cout << "0";
cout << d[r].day << "/" << d[r].year << " ";
if (d[r].hour < 10)
cout << "0";
cout << d[r].hour << ":";
if (d[r].minute < 10)
cout << "0";
cout << d[r].minute << ":";
if (d[r].sec < 10)
cout << "0";
cout << d[r].sec ;
cout<<endl;
}
int main()
{
cin>>T;
int ci=1;//记录第几次大循环
for(;ci<=T;ci++)
{
if(ci==1);else cout<<endl;
memset(s,0,sizeof(s));//初始化
memset(e,0,sizeof(e));
memset(d,0,sizeof(d));
memset(num,0,sizeof(num));
Date etime(2200,1,1,0,0,0),sime(1800,1,1,0,0,0);
ffree=0;
int flag=0;
d[flag++]=sime;
d[flag++]=etime;
int m;cin>>m;//人数
for(int i=0;i<m;i++)
{
int n;cin>>n;//任务数
num[i]=n;
for(int j=0;j<n;j++)//输入
{
cin>>s[i][j].year>>s[i][j].month>>s[i][j].day>>s[i][j].hour>>s[i][j].minute>>s[i][j].sec;
cin>>e[i][j].year>>e[i][j].month>>e[i][j].day>>e[i][j].hour>>e[i][j].minute>>e[i][j].sec;
string name;
getline(cin, name);
d[flag++]=s[i][j];//开始
d[flag++]=e[i][j];//最后
//将所有时间点存到d
}
}
cout<<"Scenario #"<<ci<<":"<<endl;//记录第几次大循环
sort(d,d+flag);//对所有人的所有任务排序,前面结构体中已定义好排序方法
int l=0,r=0,co=0,temp=0;
while(l<flag&&r<flag)
{
//1、如果某人的任务数为0,当前时间段有空的人数加1
//2、如果当前时间段小于某人的最早任务的开始时间或者晚于最后一个任务的结束时间,则当前时间段有空的人数加1.
//3、对于某人的所有任务,如果该时间段在上一个任务结束之后并且在下一个任务开始之前,则当前时间段有空的人数加1.
//如果当前时间段有时间的人数大于等于2,并且大于等于总人数减1,则该区间可能有效。最后再判断该区间
//的时间是否大于等于1小时,如果成立,则输出该区间,否则,去找下一个区间。
while(l<flag&&!search(l,m)) l++;//找到第一个符合条件的左端点
r=l;
while(r<flag&&search(r,m)) r++;//找到第一个不符合条件的右端点
if(r==flag)
{
r--;
temp=1;
}
if(onehour(d[l],d[r]))
{//判断是否为1小时,若为1小时,输出
print(l,r);
co++;//可执行的时间段+1
}
r=r+temp,l=r;//开始搜索下一个时间段
}
if(co==0)//没有一个时间段可行
cout<<"no appointment possible"<<endl;
}
}
上上次的模测
A:五一快乐
寻找含有两个0一个1一个5的且价格最低的字符串,记录这是第几个字符串,最后输出
B : 信息破译
若X为字母,则信息中的所有字母和数字均按字母表顺序往后X位,比如第一的字符为B,其字母表顺序为第二位,则信息中的所有字母和数字,均要往后两位。
若X为数字,则信息中的所有字母和数字均要往后X位。比如第一个字符为4,则信息中的所有字母和数字均要往后4位。
若X为其他字符,则X与字母A等价,
1、检查第一个字符是什么,根据字符的不同按题意分成三种情况(数字、大写字母和其他字符)
2、因为第一个字符在字符变换的过程中会发生改动,所以开始时需要将它的值保存
3、将数字和字符分别根据第一个字符变换,越界的地方循环处理
4、最后输出转换的字符串
C : 双创开门
每天减少等价于每天增多,
创建两个数组一个用来记录人数,一个用来记录该人最新一次看到的数字,按天数建立循环,每天更新该天来到的人看到的数字,只有某人在号码没更新的情况下出现两次号码才更新
最后通过比较每个人看最后看到的号码选取最大的号码
#include <bits/stdc++.h>
int a[1000001],b[1000001];//a是第i人看到的号,b存放人
int maxx,n,m,ans;
using namespace std;
int main ()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&m);
maxx=0;
for(int j=1;j<=m;++j)
scanf("%d",&b[j]);
for(int j=1;j<=m;j++)
maxx=max(maxx,a[b[j]]);//上次他们各自看到的号码,只有当某个人在maxx还没更新的时候又出现一次时它看到的号码才有可能大于maxx,因为有 a[b[j]]=maxx+1;这时候maxx更新。
for(int j=1;j<=m;++j)
a[b[j]]=maxx+1;//每个人看到的数字被更新,但maxx并没有被更新
}
for(int i=1;i<=1000000;i++)
maxx=max(maxx,a[i]);
printf("%d\n",maxx);
}
第十周
A : 小明上学
#include<bits/stdc++.h>
using namespace std;
int a,r,y,g;//红黄绿
int n;
int k,t;
int main()
{
cin>>r>>y>>g;
cin >> n;
for(int i=0;i<n;++i){
cin>>k>>t;
if(k == 1||k == 0)
a+=t;
else if(k==2) a=a+t+r;
}
printf("%d",a);
}
0道路,123红黄绿
道路和红灯直接加时间,绿灯相当于没有时间,红灯需要等黄灯剩余量+等红灯全部
B : 小中大
先排序,取头尾为最大最小值,判奇偶,奇数取中间值,偶数中间两值取平均值,若有小数的话,输出结果时设置精度为1的浮点数
#include<bits/stdc++.h>
using namespace std;
int a[10000001];
int main()
{
int n;
cin>>n;
int nn=n;
int i=0;
while(nn--)
{
cin>>a[i++];
}
sort(a,a+n);
int mi=a[0];
int ma=a[n-1];
float z;
if(n%2==0) z=(a[n/2]+a[n/2-1])/2.0;
else z=a[n/2];
int precision=0;
if (fabs(z - (int)z) > 1e-5)precision = 1;
cout<<ma<<" ";
cout<<setiosflags(ios::fixed) << setprecision(precision) <<z<<" ";
cout<<mi<<endl;
}
C : 树状数组
输入初值相当于更新值
1时执行更新操作,2时用前缀和求和
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) x&(-x)
const int maxx=1000005;
ll a[maxx],tr[maxx]; //对应原数组和树状数组
int n,q;
int k,x,y;
//更新
void updata(int i,int k)
{
//在i处加k
while(i<=n)
{
tr[i]=tr[i]+k,i+=lowbit(i);
}
}
//前缀和
ll getsum(int x)
{
ll ans=0;
while(x>0)
{
ans+=tr[x],x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
updata(i,a[i]);//输入初值相当于更新值
}
for(int i=1;i<=q;i++)
{
scanf("%d%d%d",&k,&x,&y);
if(k==1)
updata(x,y);
else
{ll ans=getsum(y)-getsum(x-1);
printf("%lld\n",ans);
}
}
}
D : 排名
将原区间划分到不可再分即l==r(只有一个元素)时,需要返回到上一次划分的时候,即区间里只有两个元素,这时左区间和右区间必定有序(因为左区间和右区间都只有一个元素),然后我们对这段区间进行排序,维护该段区间的整体有序性,
设置两个数组一个记录有每个人的成绩,一个记录排每个成绩的人数
先对输入的数对进行排序后,使用cdq分治,每次从中间划分左区域和右区域进行递归,维护每次递归划分的左区间和右区间的有序性,最后使得整个区间有序,同时通过数组记录每个人的成绩。
最后统计每个成绩有多少人。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
int ans[maxn],ans2[maxn];ans各个程序记录排多少名,ans2记录排各个名次的有多少人
typedef struct node{
int a,b,id;
}N;
N a[maxn],a2[maxn];//作为更新的中转数组
void f(int l,int r)
{
if(l==r)//左端点等于右端点,即为叶子结点,直接返回
return ;
int mid=(l+r)/2;
f(l,mid);//分别对左子节点区间和右子节点区间进行递归
f(mid+1,r);
int p=l,q=mid+1;//p是左边的起点,即第一个起点,q是第二个起点
int cnt=l-1;
while(p<=mid&&q<=r)//若两个起点都没有到达自己的右边界
{
if(a[p].a>a[q].a)
{ans[a[q].id]+=p-l;
a2[++cnt]=a[q++];//q向前推进,直到r
}
if(a[p].a<=a[q].a)
a2[++cnt]=a[p++];//p向前推进直到mid
}
while(p<=mid) //若第一个还没有到达
a2[++cnt]=a[p++];
while(q<=r) //若第二个起点还没有到达
ans[a[q].id]+=mid-l+1,a2[++cnt]=a[q++];
for(int i=l;i<=r;i++)
a[i]=a2[i];
}
bool cmp(N x,N y)//用于sort排序,注意a,b谁先谁后。
{
if(x.b!=y.b) return x.b<y.b;
else return x.a<y.a;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)//初始化
{scanf("%d%d",&a[i].a,&a[i].b);
a[i].id=i;
}
sort(a+1,a+1+n,cmp);//排序
f(1,n);//排名
for(int i=1;i<=n;i++)
ans2[ans[i]]++;//排多少名的有多少人
for(int i=0;i<n;i++)
printf("%d\n",ans2[i]);
}
E : 火星饭店
Q点菜,利用线段树进行区间查询
//a=query(1,m,1,sum-c+1,sum);当前节点区间的左右端点设置为1和目前的操作次数,因为手机更新的菜谱是逆序的,所以操作节点区间设置为最后的c个数。根据区间的包含条件递归的进行查询
A加菜,执行更新操作,同时对父节点的所记录的最大美味度信息进行更新,美味度的计算需要用到上一次屏幕上出现的的最高美味度,所以需要对其进行记录
每次的点菜都输出屏幕中的最大美味度
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
int a[maxn],t[maxn/2];//a为初始区间,t为创建的线段树
struct point
{
int l, r,a; //区间[l, r]即其中的最大值
};
point tree[1000010];
//更新
void updata(int l,int r,int x,int p,int v)
//p为下标,v为要加上的值,l,r为结点区间,x为结点下标
{
if(l==r)//左端点等于右端点,即为叶子结点,直接加v,原数组和线段树数组都得到更新
{a[x] += v,t[x] += v;
return;
}
//mid是中间点,
//左子结点区间为[l,mid],右子结点区间为[mid+1,r]
int mid=(l+r)/2;
if(p<=mid)//需要更新的结点在左子树区间
updata(l,mid,x<<1,p,v);
else //需要更新的结点在右子树区间
updata(mid+1,r,x<<1|1,p,v);
t[x]=max(t[x<<1],t[x<<1|1]); //更新父节点
}
//递归方式区间查询[L,R]的答案
int query(int l,int r,int x,int L,int R)
//通过l,r为当前结点区间,LR为操作节点区间,x为查询的结点下标//当前节点编
//a=query(1,m,1,sum-c+1,sum);通过线段树进行查找,当前节点区间的左右端点设置为1和目前的操作次数,因为手机更新的菜谱是逆序的,所以选择最后c个数,操作节点区间设置为最后的c个数。
{
if(L<=l&&r<=R)
//如果当前结点的区间真包含于要查询的区间,则返回结点信息且不需要往下递归
return t[x];
int mid=(l+r)/2;
int res=-1;//返回值变量
if(R<=mid)//要查询的区间完全在左区间
res=query(l,mid,x<<1,L,R);
else if(L>mid) //要查询的区间完全在右区间
res=query(mid+1,r,x<<1|1,L,R);
else //要查询的区间分布在左右量表
res=max(query(l,mid,x<<1,L,R),query(mid+1,r,x<<1|1,L,R));
return res;
}
int m,p;//操作数和美味度上限
int main ()
{
int sum=0;//菜品数量
scanf("%d %d",&m,&p);
char s;
int c;
int a=0;//美味度
for(int i=1;i<=m;i++)
{
cin>>s>>c;
scanf("%d",&c);
if(s=='Q'){
a=query(1,m,1,sum-c+1,sum);
printf("%d\n",a);
}else if(s=='A')
{
c=(c+a)%p;
updata(1,m,1,++sum,c);
}
}
}
标签:files,文件,return,保命,验收,int,now,size 来源: https://blog.csdn.net/weixin_45982173/article/details/117093345