"蔚来杯"2022牛客暑期多校训练营3
C Concatenation
题目大意
给定n个字符串,求一个将他们拼接起来的方案,使得结果的字典序最小。
题解
对n个字符串排序,a比b小的条件是a+b<b+a。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 6;
bool cmp(const string &a, const string &b)
{
return a + b < b + a;
}
string s[maxn];
int n;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
cin >> s[i];
sort(s, s + n, cmp);
for (int i = 0; i < n; i++)
{
cout << s[i];
}
return 0;
}
J Journey
题目大意
给定一个城市有若干十字路口,右转不需要等红灯,直行、左转和掉头都需要,求起点到终点最少等几次红灯。
题解
建图:把每个十字路口的四个方向的路口信息存储在一个数组中。
然后 bfs 求出最短路径。
bfs的细节:
1、定义一个dis数组,存储从起点到当前点的距离信息(例如dis[i] [j]表示从与j路口相邻的第i个路口这个方向,到j这个路口,距起点的距离);定义一个vis数组,存储入队信息(spfa),vis数组表示的信息类似于dis数组。
2、初始化dis[相对于s2,s1在s2的哪个方向] [s2]为0,vis[相对于s2,s1在s2的哪个方向] [s2]为1。
3、对于队首元素,遍历它的每个方向,更新dis和vis,即spfa。
4、最后如果dis[相对于t2,t1在t2的哪个方向] [t2]更新过,则输出答案,否则输出-1。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 5;
const int Max = 0x3f3f3f3f;
typedef pair<int, int> P;
int n, s1, s2, t1, t2, dir1, dir2;
struct Node
{
int a[5];
} e[maxn];
int dis[5][maxn];
bool vis[5][maxn];
void bfs()
{
queue<P> q;
for (int i = 1; i <= 4; i++)
if (e[s2].a[i] == s1)
dir1 = i;
for (int i = 1; i <= 4; i++)
if (e[t2].a[i] == t1)
dir2 = i;
q.push(P(dir1, s2));
vis[dir1][s2] = 1;
dis[dir1][s2] = 0;
while (!q.empty())
{
P now = q.front();
q.pop();
int start = now.second, tdir = now.first;
vis[tdir][start] = 0;
for (int i = 1; i <= 4; i++)
{
int w = 1, v = e[start].a[i], vdir;
if (v == 0)
continue;
if (i == tdir % 4 + 1)
w = 0;
for (int j = 1; j <= 4; j++)
if (e[v].a[j] == start)
vdir = j;
if (dis[vdir][v] > dis[tdir][start] + w)
{
dis[vdir][v] = dis[tdir][start] + w;
if (!vis[vdir][v])
q.push(P(vdir, v)), vis[vdir][v] = 1;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(dis, Max, sizeof dis);
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 4; j++)
cin >> e[i].a[j];
cin >> s1 >> s2 >> t1 >> t2;
bfs();
if (dis[dir2][t2] != Max)
cout << dis[dir2][t2] << endl;
else
cout << -1 << endl;
return 0;
}
A Ancestor
题目大意
给出两棵编号1-n的树A和B,A、B树上每个节点均有一个权值,给出k个关键点的编号x1…xn,问有多少种方案使得去掉恰好一个关键点使得剩余关键点在树A上LCA的权值大于树B上LCA的权值。
题解
树剖求LCA。
预处理出关键点序列在树A、B上的前缀LCA和后缀LCA,枚举去掉的关键节点并使用前后缀LCA算出剩余节点的LCA比较权值即可。
前缀LCA指前n个点的LCA,后缀LCA指后n个点的LCA。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int va[maxn], vb[maxn], x[maxn];
int n, k, res;
struct tree
{
vector<int> g[maxn];
int fa[maxn], size[maxn], top[maxn], mson[maxn], deep[maxn];
int pre[maxn], lst[maxn];
void add(int x, int y)
{
g[x].push_back(y);
}
void dfs(int x)
{
size[x] = 1;
for (int i = 0; i < g[x].size(); i++)
{
int y = g[x][i];
fa[y] = x;
deep[y] = deep[x] + 1;
dfs(y);
size[x] += size[y];
if (size[y] > size[mson[x]])
mson[x] = y;
}
}
void dfs(int x, int y)
{
top[x] = y;
if (mson[x])
dfs(mson[x], y);
for (int i = 0; i < g[x].size(); i++)
{
int y = g[x][i];
if (y == mson[x])
continue;
dfs(y, y);
}
}
int lca(int x, int y)
{
while (top[x] != top[y])
{
if (deep[top[x]] < deep[top[y]])
swap(x, y);
x = fa[top[x]];
}
if (deep[x] < deep[y])
return x;
return y;
}
void predo()
{
dfs(1);
dfs(1, 1);
pre[1] = x[1];
for (int i = 2; i <= k; i++)
{
pre[i] = lca(pre[i - 1], x[i]);
}
lst[k] = x[k];
for (int i = k - 1; i >= 1; i--)
{
lst[i] = lca(lst[i + 1], x[i]);
}
}
int getlca(int x)
{
if (x == 1)
return lst[2];
if (x == k)
return pre[k - 1];
return lca(pre[x - 1], lst[x + 1]);
}
} a, b;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= k; i++)
cin >> x[i];
for (int i = 1; i <= n; i++)
cin >> va[i];
for (int i = 2; i <= n; i++)
{
int fa;
cin >> fa;
a.add(fa, i);
}
for (int i = 1; i <= n; i++)
cin >> vb[i];
for (int i = 2; i <= n; i++)
{
int fb;
cin >> fb;
b.add(fb, i);
}
a.predo();
b.predo();
for (int i = 1; i <= k; i++)
{
int wa = a.getlca(i), wb = b.getlca(i);
if (va[wa] > vb[wb])
res++;
}
cout << res << endl;
}
F Fief
题目大意
给定一个无向图,每次询问两点x、y,求是否存在一个n的排列,使得第一个元素为x,最后一个元素为y,且排列的任意一个前缀、任意一个后缀都连通(前缀不包括y,后缀不包括x)。
题解
代码
未完