AT_abc411_d 题解
题目描述
有一个服务器和 N 台 PC,编号为 1,2,3,..., N。服务器和每台 PC 各自持有一个字符串,初始时所有字符串都为空。
给出 Q 个查询。每个查询是以下格式之一:
1 p:将 p 号 PC 的字符串的替换为服务器的字符串。
2 p s:将字符串 s 加到 PC 中的字符串 p 的的末尾。
3 p:将服务器的字符串替换为 p 号 PC 的字符串。
按照给定的顺序处理所有查询后,找出服务器的最终字符串。
数据范围:
1 <= N, Q <= 2*10^5
对于每个查询,1 <= p <= N。
对于每个 2 类型的查询,s 是一个长度至少为 1 的字符串,由小写英文字母组成。
在所有类型为 2 的查询中,s 的长度总和最多为 10^6
输入格式
第一行输入两个整数 N 和 Q。
接下来的 Q 行:每行一个查询,格式如上所述。
输出格式
输出服务器的最终字符串。
思路
首先,我们可以按照题目要求模拟,这个非常简单,不必多讲,就是利用了STL string
#include <iostream>
using namespace std;
string pc[200001]; // PC
string server; // 服务器
int main()
{
int n, q;
cin >> n >> q;
while (q--)
{
int op;
cin >> op;
if (op == 1)
{
int p;
cin >> p;
pc[p] = server; // 按照题目模拟
}
else if (op == 2)
{
int p;
string s;
cin >> p >> s;
pc[p] += s; // 按照题目模拟(可以利用STL string 拼接的功能)
}
else
{
int p;
cin >> p;
server = pc[p]; // 按照题目模拟
}
}
cout << server << endl;
return 0;
}
这份代码短小精悍,获得了以下的好成绩

嘶—— 为什么呢?这份代码似乎已经达到了最快的效果了啊!
哦!!!字符串的赋值与拼接都是 O(size) 的,肯定会超时啊!
嘶—— 那这怎么办?
哦!!!我们可以先把文本中出现的字符串用数组存起来,pc和服务器就只需要存储数组中字符串的下标,在做赋值操作时就可以接近O(1)了!
等一下!那拼接的时候怎么记住这个字符串拼接到了这里?
嘶—— 那怎么办呢?
哦!!!我们可以记住这个字符串的上一段字符串的编号,然后pc的下标就改为这个字符串的编号。
当需要遍历整个字符串时,就可以一步一步往上跳,把每一段的字符串接在一起,就可以了。
所以现在一个下标就能表示一个字符串。
于是我们就能写出代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int pc[200001];
int server;
string str[200001];
int fa[200001];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
int cnt = 0;
for (int i = 1; i <= q; i++)
{
int op;
cin >> op;
if (op == 1)
{
int p;
cin >> p;
pc[p] = server; // 将下标赋值给server,因为一个下标就能表示一个字符串。
}
else if (op == 2)
{
cnt++;
int p;
cin >> p >> str[cnt];
fa[cnt] = pc[p]; // 记住这个字符串之前的下标
pc[p] = cnt; // 更新下标
}
else
{
int p;
cin >> p;
server = pc[p]; // 将下标赋值给pc[p],因为一个下标就能表示一个字符串。
}
}
int p = server; // 当前下标
string ans;
while (p != 0)
{
ans = str[p] + ans; // 每一次在头上加上前面的字符串(不在后面加,要在头上加,因为越往前跳,这个字符串是越早加的)
p = fa[p]; // 往前跳
}
cout << ans << endl;
return 0;
}
你以为结束了?没有!这份代码获得了下面的好成绩:

嘶—— 咋又出问题了?这份代码似乎已经无懈可击了啊!
究竟是哪里出问题了?
哦!!!输出的时候又出现了赋值和拼接!每一次拼接都使字符串变得更长,导致赋值操作一次更比一次慢!
嘶—— 那这怎么办?
那我们可以直接输出吗?
不行!字符串得从前往后输出,也就是说后跳到的先输出!不能先跳到的直接输出!
哦!!!那把跳到的字符串依次先用vector存起来,然后将vector从后往前输出就可以了。
可是那也要赋值啊!
不用担心!这次赋值之后没有拼接,不会导致每一次都使字符串更长。也就是说耗时最多也只能是所有短字符串长度之和(题目中说了 <= 10^6)
那就可以放心的写代码了:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int pc[200001];
int server;
string str[200001];
int fa[200001];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
int cnt = 0;
for (int i = 1; i <= q; i++)
{
int op;
cin >> op;
if (op == 1)
{
int p;
cin >> p;
pc[p] = server; // 将下标赋值给server,因为一个下标就能表示一个字符串。
}
else if (op == 2)
{
cnt++;
int p;
cin >> p >> str[cnt];
fa[cnt] = pc[p]; // 记住这个字符串之前的下标
pc[p] = cnt; // 更新下标
}
else
{
int p;
cin >> p;
server = pc[p]; // 将下标赋值给pc[p],因为一个下标就能表示一个字符串。
}
}
int p = server;
vector<string> ansv; // 存储小字符串
while (p != 0)
{
ansv.push_back(str[p]); // 依次放入
p = fa[p];
}
for (int i = ansv.size() - 1; i >= 0; i--) // 倒序输出
{
cout << ansv[i];
}
cout << endl;
return 0;
}
这份代码获得了以下的好成绩:

而且由于最根本的问题解决了,所以运行速度飙到了39ms
但是!真结束了吗?
确实结束了,点个赞呗!
感谢大佬们可以看完蒟蒻又臭又长的题解。

浙公网安备 33010602011771号