比赛地址
弱校连萌寒假专题一
题解前继
2015弱校连萌寒假专题一 题解(A-J)
K. Alignment of Code
题意:
给定n行文章,每行是由空格分隔的一些单词,请你适当地增删空格,使得每一列的单词在每一行的开始位置相同,且每一列最长的单词和它后面的单词只有一个空格分隔。
n <= 1000, 单词长度 <= 80, 一行文章长度 <= 180。
题解:
读入n行文章之后计算每列最长单词长度,按要求输出即可。注意不能有多余的行末空格。时间复杂度O(字符数)。
代码:
#include <cstdio>
#include <cstring>
const int maxn = 1010, maxl = 200;
int t, n, cnt[maxn], sta[maxn][maxl], end[maxn][maxl], maxlen[maxl];
char str[maxn][maxl];
int max(int x, int y)
{
return x < y ? y : x;
}
int main()
{
scanf("%d\n", &t);
while(t--)
{
memset(cnt, 0, sizeof cnt);
memset(maxlen, 0, sizeof maxlen);
for(n = 0; gets(str[n]) != NULL; ++n)
if(str[n][0] == '@' && str[n][1] == '\0')
break;
for(int i = 0; i < n; ++i)
{
for(int j = 0, k = 0; str[i][j]; j = k)
{
while(str[i][j] == ' ')
++j;
k = j;
while(str[i][k] && str[i][k] != ' ')
++k;
if(j < k)
{
sta[i][cnt[i]] = j;
end[i][cnt[i]] = k;
maxlen[cnt[i]] = max(maxlen[cnt[i]], k - j);
++cnt[i];
}
}
}
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < cnt[i]; ++j)
{
if(j)
putchar(' ');
str[i][end[i][j]] = '\0';
if(j + 1 == cnt[i])
printf("%s", str[i] + sta[i][j]);
else
printf("%-*s", maxlen[j], str[i] + sta[i][j]);
}
putchar('\n');
}
}
return 0;
}
L. Ducci Sequence
题意:
给定长度为n的数列{a_n},每次将{a_1, a_2, ..., a_n}变成{|a_1 - a_2|, |a_2 - a_3|, ..., |a_n - a_1|},问这样的操作是否会变成{0, 0, ..., 0},还是会一直循环下去。
n <= 15, 若有循环节则一定在1000步操作内出现。
题解:
直接模拟前1000次操作,每次检查当前数列是否出现过,利用Hash可以做到时间复杂度O(kn),k >= 1000。
代码:
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 16;
int n;
struct DucciSeq
{
int a[maxn];
bool operator < (const DucciSeq &x) const
{
for(int i = 0; i < maxn; ++i)
if(a[i] != x.a[i])
return a[i] < x.a[i];
return 0;
}
bool empty() const
{
for(int i = 0; i < n; ++i)
if(a[i])
return 0;
return 1;
}
} x;
map<DucciSeq, int> Hash;
int abs(int x)
{
return x < 0 ? -x : x;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
memset(&x, 0, sizeof x);
Hash.clear();
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%d", &x.a[i]);
if(x.empty())
{
puts("ZERO");
continue;
}
Hash[x] = 1;
for(int t = 1; t <= 1000; ++t)
{
int tmp = x.a[0];
for(int i = 0; i < n - 1; ++i)
x.a[i] = abs(x.a[i] - x.a[i + 1]);
x.a[n - 1] = abs(x.a[n - 1] - tmp);
if(x.empty())
{
puts("ZERO");
break;
}
else if(Hash[x])
{
puts("LOOP");
break;
}
Hash[x] = 1;
}
}
return 0;
}
M. The Letter Carrier's Rounds
题意:
给定SMTP的工作方式,模拟MTA发送方的SMTP操作,包括MTA接收方的回馈信息。
题解:
记录每个MTA的用户名便于快速查找,然后按照题意模拟即可。
代码:
#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef map<string, int> Users;
map<string, Users> MTA;
string sender, text;
map<string, int> idx, vis;
vector<vector<string> > recver;
int main()
{
ios::sync_with_stdio(0);
while(cin >> text && text != "*")
{
int cnt;
Users tmp;
tmp.clear();
cin >> text >> cnt;
while(cnt--)
{
cin >> sender;
tmp[sender] = 1;
}
MTA[text] = tmp;
}
while(cin >> sender && sender != "*")
{
int cnt = 0;
string::size_type pos = sender.find('@');
string senderMTA = sender.substr(pos + 1), recverMTA, tmp;
idx.clear();
vis.clear();
for(vector<vector<string> >::iterator it = recver.begin(), jt = recver.end(); it < jt; ++it)
(*it).clear();
recver.clear();
while(cin >> tmp && tmp != "*")
{
recverMTA = tmp.substr(tmp.find('@') + 1);
if(vis.find(tmp) != vis.end())
continue;
vis[tmp] = 1;
if(idx.find(recverMTA) == idx.end())
{
idx[recverMTA] = cnt++;
vector<string> name;
name.clear();
name.push_back(tmp);
recver.push_back(name);
}
else
recver[idx[recverMTA]].push_back(tmp);
}
text = "";
getline(cin, tmp);
while(getline(cin, tmp) && tmp != "*")
text += " " + tmp + "\n";
Users List = MTA[senderMTA];
if(List.find(sender.substr(0, sender.find('@'))) == List.end())
continue;
for(vector<vector<string> >::iterator it = recver.begin(), jt = recver.end(); it < jt; ++it)
{
vector<string> &temp = *it;
recverMTA = temp[0].substr(temp[0].find('@') + 1);
List = MTA[recverMTA];
cout << "Connection between " + senderMTA + " and " + recverMTA << endl;
cout << " HELO " + senderMTA << endl;
cout << " 250" << endl;
cout << " MAIL FROM:<" + sender + ">" << endl;
cout << " 250" << endl;
bool flag = 0;
for(vector<string>::iterator it = temp.begin(), jt = temp.end(); it < jt; ++it)
{
cout << " RCPT TO:<" + *it + ">" << endl;
if(List.find((*it).substr(0, (*it).find('@'))) != List.end())
{
flag = 1;
cout << " 250" << endl;
}
else
cout << " 550" << endl;
}
if(flag)
{
cout << " DATA" << endl;
cout << " 354" << endl;
cout << text << " ." << endl;
cout << " 250" << endl;
}
cout << " QUIT" << endl;
cout << " 221" << endl;
}
}
return 0;
}
N. Symmetry
题意:
给定平面上n个点,问是否存在一条直线x = k使得这n个点关于x = k对称。
n <= 1000。
题解:
若存在该直线,则k一定为这n个点横坐标的平均值,算出来之后检验这n个点的对称点是否存在即可,时间复杂度O(n)。
由于这n个点都是整点,k一定是0.5的倍数,如果想规避浮点数运算的误差,可以将坐标全部放大两倍进行整数运算。
代码:
#include <map>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int, int> point;
map<point, int> Hash;
int t, n, minx, maxx, xx;
int main()
{
scanf("%d", &t);
while(t--)
{
Hash.clear();
minx = ~0u >> 1, maxx = -minx;
scanf("%d", &n);
for(int i = 0; i < n; ++i)
{
int x, y;
scanf("%d%d", &x, &y);
++Hash[make_pair(x <<= 1, y <<= 1)];
if(x < minx)
minx = x;
if(maxx < x)
maxx = x;
}
xx = minx + maxx;
bool flag = 0;
for(map<point, int>::iterator it = Hash.begin(), jt = Hash.end(); it != jt; ++it)
{
point tmp = it -> first;
tmp.first = xx - tmp.first;
if(Hash.find(tmp) == jt || Hash[tmp] != it -> second)
{
flag = 1;
break;
}
}
puts(flag ? "NO" : "YES");
}
return 0;
}
O. Printer Queue
题意:
给定一个长度为n的操作队列,对于队首元素,若优先级为最高,则做该事件,否则将之置于队尾等待以后操作,现在求第k位的事件会是第几个被操作的。
题解:
统计每个优先级还剩多少个操作,按照队列的定义模拟即可,每次找到需要做的操作至多将队列元素全部遍历一遍,所以时间复杂度O(n^2)。
由于任意时刻队列的元素个数不超过n,所以可以用循环队列做到空间复杂度O(n)。
代码:
#include <cstdio>
const int maxn = 110;
int t, n, pos, que[maxn], l, r, now, cnt[11], ans;
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &pos);
l = 0, r = n, now = 1, ans = 0;
for(int i = 1; i <= 9; ++i)
cnt[i] = 0;
for(int i = 0; i < n; ++i)
{
scanf("%d", que + i);
if(now < que[i])
now = que[i];
++cnt[que[i]];
}
while(l != r)
if(que[l] == now)
{
++ans;
if(l == pos)
break;
--cnt[que[l++]];
if(l >= maxn)
l -= maxn;
while(now && !cnt[now])
--now;
}
else
{
if(l == pos)
pos = r;
que[r++] = que[l++];
if(l >= maxn)
l -= maxn;
if(r >= maxn)
r -= maxn;
}
printf("%d\n", ans);
}
return 0;
}
P. Borrowers
题意:
现在有一个图书馆,藏书按照(作者名, 书名)的形式按字典序排序,现在请你维护三种操作:借书,从藏书中删去某书;还书,将某书放入待整理区;整理,将待整理区的书排序后依次插入藏书,每次输出插入的位置在哪本书之后,或是插到最前面。
题解:
记录每本书的作者,待整理区需要整理时排序即可,藏书库用平衡树维护,每次查找插入位置即可。
代码懒得手写平衡树,使用了STL的set。设操作数为n,可以做到时间复杂度O(nlogn)。
代码:
#include <map>
#include <set>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<string, string> Book;
map<string, string> Dict;
set<Book> Lib, Rcv;
string tmp1, tmp2;
int main()
{
ios::sync_with_stdio(0);
while(getline(cin, tmp1) && tmp1 != "END")
{
string::size_type pos = tmp1.rfind("\"");
tmp2 = tmp1.substr(pos + 5);
tmp1 = tmp1.substr(1, pos - 1);
Dict[tmp1] = tmp2;
Lib.insert(make_pair(tmp2, tmp1));
}
while(getline(cin, tmp1) && tmp1 != "END")
if(tmp1[0] == 'B')
{
tmp2 = tmp1.substr(8, tmp1.size() - 9);
Lib.erase(make_pair(Dict[tmp2], tmp2));
}
else if(tmp1[0] == 'R')
{
tmp2 = tmp1.substr(8, tmp1.size() - 9);
Rcv.insert(make_pair(Dict[tmp2], tmp2));
}
else
{
for(set<Book>::iterator it = Rcv.begin(), jt = Rcv.end(); it != jt; ++it)
{
set<Book>::iterator pos = Lib.insert(*it).first;
if(pos == Lib.begin())
cout << "Put \"" + it -> second + "\" first" << endl;
else
cout << "Put \"" + it -> second + "\" after \"" + (--pos) -> second + "\"" << endl;
}
cout << "END" << endl;
Rcv.clear();
}
return 0;
}
Q. Bug Hunt
题意:
给定一篇特定的代码,请你检查它是否有特定的BUG,若有输出第一个BUG的行数,若无输出0。
代码只包含一下两种语句:
<array_name>[<expression>],表示定义一个数组,数组名叫<array_name>,长度为<expression>,下标从0开始。
<array_name>[<expression1>]=<expression2>,表示对数组<array_name>的第<expression1>的位置的元素赋值为<expression2>。
其中<array_name>必须是英文字母,<expression>、<expression1>、<expression2>表示表达式,表达式可以是数组元素,也可以是非负整数。
特定的BUG包括下标越界,数组元素未定义,数组未定义三种。
题解:
递归求解expression,注意三种BUG的情况即可。
数组可以用map的映射功能代替。
代码:
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int size[52], now, ans;
map<int, int> array[52];
int trans(char ch)
{
return ch <= 'Z' ? ch - 'A' + 26 : ch - 'a';
}
int GetValue(string a)
{
if(a.find('[') == string::npos)
{
int ret = 0;
for(int i = 0, j = a.size(); i < j; ++i)
ret = (ret << 3) + (ret << 1) + a[i] - '0';
return ret;
}
int name = trans(a[0]), idx = GetValue(a.substr(2, a.size() - 3));
if(idx == -1 || idx >= size[name] || array[name].find(idx) == array[name].end())
return -1;
return array[name][idx];
}
int main()
{
ios::sync_with_stdio(0);
string tmp;
while(cin >> tmp)
if(tmp == ".")
{
if(now)
cout << ans << endl;
ans = now = 0;
for(int i = 0; i < 52; ++i)
if(size[i])
{
array[i].clear();
size[i] = 0;
}
}
else
{
++now;
if(ans)
continue;
string::size_type pos = tmp.find('=');
if(pos == string::npos)
{
int name = trans(tmp[0]), idx = GetValue(tmp.substr(2, tmp.size() - 3));
if(idx == -1)
{
ans = now;
continue;
}
size[name] = idx;
}
else
{
int name = trans(tmp[0]), idx = GetValue(tmp.substr(2, pos - 3)), value = GetValue(tmp.substr(pos + 1));
if(idx == -1 || idx >= size[name] || value == -1)
{
ans = now;
continue;
}
array[name][idx] = value;
}
}
return 0;
}
R. Searching the Web
题意:
给定n篇文章,单词由英文字母组成,有m个单词查找操作,操作分为四种:
word,查找带有word的文章,输出对应文章内含有term的行。
NOT word,查找不带有word的文章,输出整篇文章。
word1 AND word2,查找既带有word1又带有word2的文章,输出对应文章内含有word1或word2的行。
word1 OR word2,查找带有word1或带有word2的文章,输出对应文章内含有word1或word2的行。
查询结果为空输出"Sorry, I found nothing.",单次查询中不同文章之间由一行"----------"分隔,每次查询末尾带一行"=========="。
n < 100, m <= 50000, 一行字符数不超过80, 文章行数之和不超过1500。
题解:
对每个单词记录每一行是否出现过,按照四种逻辑直接操作即可。
设文章行数之和为k,时间复杂度为O(mk)。
代码:
#include <map>
#include <bitset>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxr = 1510, maxc = 88;
bitset<maxr> ZERO(0), ONE(1);
map<string, int> Hash;
int n, r, m, mark[maxr], tot;
vector<bitset<maxr> > mask;
char str[maxr][maxc];
bitset<maxr> flip(bitset<maxr> tmp)
{
return tmp.flip();
}
void Find(string s, bool flag) // ·Ç = 1
{
bitset<maxr> mass(0);
bitset<maxr> &res = Hash.find(s) != Hash.end() ? mask[Hash[s]] : ZERO;
for(int i = 0; i < r; ++i)
mass[mark[i]] = mass[mark[i]] | res[i];
bool flag2 = 1;
for(int i = 0; i < n; ++i)
flag2 &= mass[i];
if(!flag && mass.none() || flag && flag2)
{
puts("Sorry, I found nothing.\n==========");
return;
}
for(int i = 0, last = -1; i < r; ++i)
if(!flag && mass[mark[i]] && res[i] || flag && !mass[mark[i]])
{
if(last != -1 && last != mark[i])
puts("----------");
puts(str[i]);
last = mark[i];
}
puts("==========");
}
void Find(string s, string t, bool flag) // Óë = 0 »ò = 1
{
bitset<maxr> mass1(0), mass2(0), mass(0);
bitset<maxr> &res1 = Hash.find(s) != Hash.end() ? mask[Hash[s]] : ZERO;
bitset<maxr> &res2 = Hash.find(t) != Hash.end() ? mask[Hash[t]] : ZERO;
for(int i = 0; i < r; ++i)
{
mass1[mark[i]] = mass1[mark[i]] | res1[i];
mass2[mark[i]] = mass2[mark[i]] | res2[i];
}
for(int i = 0; i < n; ++i)
mass[i] = flag ? mass1[i] | mass2[i] : mass1[i] & mass2[i];
if(mass.none())
{
puts("Sorry, I found nothing.\n==========");
return;
}
for(int i = 0, last = -1; i < r; ++i)
if(mass[mark[i]] && (res1[i] || res2[i]))
{
if(last != -1 && last != mark[i])
puts("----------");
puts(str[i]);
last = mark[i];
}
puts("==========");
}
int main()
{
char op[5], tmp[maxc << 2], tmp1[maxc], tmp2[maxc];
scanf("%d\n", &n);
for(int t = 0; t < n; ++t)
while(gets(str[r]) != NULL)
{
if(strcmp(str[r], "**********") == 0)
break;
int i, j;
for(i = 0, j = 0; str[r][i]; ++i)
if(str[r][i] >= 'A' && str[r][i] <= 'Z')
tmp[j++] = str[r][i] - 'A' + 'a';
else if(str[r][i] >= 'a' && str[r][i] <= 'z')
tmp[j++] = str[r][i];
else if(j)
{
tmp[j] = '\0';
string temp = tmp;
if(Hash.find(temp) == Hash.end())
{
Hash[temp] = tot++;
mask.push_back(ZERO);
}
mask[Hash[temp]][r] = 1;
j = 0;
}
if(j)
{
tmp[j] = '\0';
string temp = tmp;
if(Hash.find(temp) == Hash.end())
{
Hash[temp] = tot++;
mask.push_back(ZERO);
}
mask[Hash[temp]][r] = 1;
j = 0;
}
mark[r++] = t;
}
scanf("%d\n", &m);
while(m--)
{
gets(tmp);
if(strncmp(tmp, "NOT ", 4) == 0)
{
sscanf(tmp,"%*s%s", tmp1);
Find(tmp1, 1);
}
else if(strchr(tmp, ' ') == NULL)
Find(tmp, 0);
else
{
sscanf(tmp, "%s%s%s", tmp1, op, tmp2);
if(strcmp(op, "AND") == 0)
Find(tmp1, tmp2, 0);
else
Find(tmp1, tmp2, 1);
}
}
return 0;
}
S. Exchange
题意:
有n个操作,操作有三种:
买操作(价格, 数量),添加一个买订单,并检查是否有卖订单比买订单价格低,按卖订单价格尽量低、挂订单时间尽量早的靠前的顺序快速买进,输出交易情况。
卖操作(价格, 数量),添加一个卖订单,并检查是否有买订单比卖订单价格高,按买订单价格尽量高、挂订单时间尽量早的靠前的顺序快速卖出,输出交易情况。
删除操作(标号),删除一个买或卖的订单,已经交易的部分不做撤销。
每次操作后输出当前买订单最高价以及最高价的产品数量、当前卖订单最低价以及最低价的产品数量,无订单时默认买订单最高价为0,卖订单最低价为99999。
n <= 10000。
题解:
对于买操作,维护卖订单以价格排序的小根堆,存储对于价格的产品数量和按时间戳排序的订单列表,将买订单对着卖订单进行尝试,尝试完毕后加入买订单的堆。
对于卖操作,维护买订单以价格排序的大根堆,存储对于价格的产品数量和按时间戳排序的订单列表,将卖订单对着买订单进行尝试,尝试完毕后加入卖订单的堆。
对于删除操作,直接找订单对应的价格,更新订单列表和产品数量,订单列表内的查找可以用二分查找但比较麻烦,可以直接写一个平衡树来维护。
时间复杂度O(nlogn),直接用map写的话常数不是一般的大。
代码:
#include <map>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 16384;
typedef map<int, int> Order;
typedef pair<int, Order> Orders;
map<int, Orders, greater<int> > Buy;
map<int, Orders, less<int> > Sell;
int n, id, p, q, P[maxn];
template<class A, class B> void Maintain(map<int, Orders, A> &act, map<int, Orders, B> &pass)
{
while(q > 0)
{
if(pass.empty())
break;
map<int, Orders, less<int> >::iterator it = pass.begin();
int pp = it -> first;
B cmp;
if(cmp(p, pp))
break;
Orders &tmp = it -> second;
Order::iterator jt = tmp.second.begin();
int qq = min(q, jt -> second);
printf("TRADE %d %d\n", qq, pp);
q -= qq;
jt -> second -= qq;
tmp.first -= qq;
if(!jt -> second)
tmp.second.erase(jt -> first);
if(!tmp.first)
pass.erase(it -> first);
}
if(q > 0)
if(act.find(p) != act.end())
{
Orders &tmp = act[p];
tmp.first += q;
tmp.second[id] = q;
}
else
{
Order tmp;
tmp.clear();
tmp[id] = q;
act[p] = make_pair(q, tmp);
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
char op[10];
scanf("%s", op);
if(op[0] == 'B' || op[0] == 'S')
{
scanf("%d%d", &q, &p);
P[i] = p;
id = i;
if(op[0] == 'B')
Maintain(Buy, Sell);
else
Maintain(Sell, Buy);
}
else
{
scanf("%d", &id);
if(Buy.find(P[id]) != Buy.end())
{
Orders &tmp = Buy[P[id]];
if(tmp.second.find(id) != tmp.second.end())
{
tmp.first -= tmp.second[id];
tmp.second.erase(id);
}
if(!tmp.first)
Buy.erase(P[id]);
}
if(Sell.find(P[id]) != Sell.end())
{
Orders &tmp = Sell[P[id]];
if(tmp.second.find(id) != tmp.second.end())
{
tmp.first -= tmp.second[id];
tmp.second.erase(id);
}
if(!tmp.first)
Sell.erase(P[id]);
}
}
int bq = 0, bp = 0, sq = 0, sp = 99999;
if(!Buy.empty())
{
map<int, Orders, greater<int> >::iterator it = Buy.begin();
bq = it -> second.first;
bp = it -> first;
}
if(!Sell.empty())
{
map<int, Orders, less<int> >::iterator it = Sell.begin();
sq = it -> second.first;
sp = it -> first;
}
printf("QUOTE %d %d - %d %d\n", bq, bp, sq, sp);
}
return 0;
}
T. Database
题意:
给定n行m列的字符串,每行的字符串之间由','分隔,问是否存在两行的两列是一样的字符串,输出任意一种可能的情况。
n <= 10000, m <= 10。
题解:
对于每个字符串求出Hash,枚举答案的两列,将每一行的两个字符串Hash值捆绑起来再Hash,看是否存在两行Hash一样。
时间复杂度O(nm^2),常数较大,和字符数有关。
代码:
#include <cstdio>
#include <cstring>
const int maxn = 10010, maxm = 11, maxl = 81, mod1 = 233, mod2 = 23333, mod = 199669;
typedef unsigned long long ULL;
int n, m, tot;
char str[maxl];
ULL Hash1[maxn][maxm];
struct Node
{
int row, nxt;
ULL H1, H2;
} e[maxn];
int Hash[mod];
int main()
{
while(scanf("%d%d\n", &n, &m) == 2)
{
for(int i = 0; i < n; ++i)
{
gets(str);
for(int j = 0, k = 0; j < m && str[k]; ++j)
{
Hash1[i][j] = 0;
for( ; str[k] && str[k] != ','; ++k)
Hash1[i][j] = Hash1[i][j] * mod1 + str[k];
if(str[k] == ',')
++k;
}
}
bool flag = 0;
for(int j = 0; j < m; ++j)
{
for(int k = j + 1; k < m; ++k)
{
tot = 0;
memset(Hash, 0, sizeof Hash);
for(int i = 0; i < n; ++i)
{
int HH = (Hash1[i][j] * mod2 + Hash1[i][k]) % mod, res;
for(int it = Hash[HH]; it; it = e[it].nxt)
if(e[it].H1 == Hash1[i][j] && e[it].H2 == Hash1[i][k])
{
flag = 1;
res = e[it].row;
break;
}
if(flag)
{
puts("NO");
printf("%d %d\n%d %d\n", res + 1, i + 1, j + 1, k + 1);
break;
}
else
{
e[++tot] = (Node){i, Hash[HH], Hash1[i][j], Hash1[i][k]};
Hash[HH] = tot;
}
}
if(flag)
break;
}
if(flag)
break;
}
if(!flag)
puts("YES");
}
return 0;
}
小记
做题顺序是GHINDLOKTJBESFPQRAMC,其中FKPRS是FB,没有按照难度升序来做,看到几个偏数据结构的题目先做了,模拟题放到了后面慢慢解决。写代码还是很慢。