"蔚来杯"2022牛客暑期多校训练营3:C, A, J
C、Concatenation 思维
题意:
给定n个字符串,请重新排列他们,使得最后组成的字符串的字典序最小。
代码:
bool cmp(string &a, string &b) {
return a + b < b + a;
}
string s[MAXN];
void slove() {
int n; cin >> n;
for (int i = 1; i <= n; i++)cin >> s[i];
sort(s + 1, s + 1 + n, cmp);
for (int i = 1; i <= n; i++)cout << s[i];
cout << endl;
}
A、Ancestor 多点LCA
前置知识:dfs序,lca
对于求多个点的最近公共祖先,我们并不真的要对所有点两两都求一次。我们只用取这些点中dfs序最小和最大的两个点来求最近公共祖先就行。
题意
求长度为k-1的点集的最近公共祖先(一个点被删了)
分析
知道了这两个前置知识后,这题就变的很简单了。
我们可以先对所有的点来一遍前序遍历,求得他们的dfs序。
-
根据dfs序对k集合的点进行升序排序(两个树的dfs序不一定一样的,所以我们两边都要求嗷,排序也是)。
-
然后我们枚举第1~第k个点作为被删除的点,对于这两个树,我们取他们k集合中dfs序最大和最小的两个点来分别求lca。如果它们中有点正好是这次被删除的点,那我们就取第二大(第二小)的点(哪个被删改哪个,没被删就不理)。
-
之后比较两边求出来的点,看A树的祖先的权值是否大于B树的祖先,如果大于,则计数器++。
复杂度分析
倍增lca预处理复杂度为:n*logn.
对k集合点排序:k*logk.
处理单次询问:logn.
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];
int arr[N];
int brr[N];
vector<int> g1[N],g2[N];
int fa1[N][20],fa2[N][20];
int dep1[N],dep2[N];
int in1[N],cnt1;
int in2[N],cnt2;
bool cmpA(int x, int y)
{
return in1[x] < in1[y];
}
bool cmpB(int x, int y)
{
return in2[x] < in2[y];
}
void dfs1(int u,int pre){
in1[u]=++cnt1;
dep1[u]=dep1[pre]+1;
fa1[u][0]=pre;
for(int i=1;(1<<i)<=dep1[u];i++){
fa1[u][i]=fa1[fa1[u][i-1]][i-1];
}
for(int t:g1[u]){
if(t!=pre) dfs1(t,u);
}
}
void dfs2(int u,int pre){
in2[u]=++cnt2;
dep2[u]=dep2[pre]+1;
fa2[u][0]=pre;
for(int i=1;(1<<i)<=dep2[u];i++){
fa2[u][i]=fa2[fa2[u][i-1]][i-1];
}
for(int t: g2[u]){
if(t!=pre) dfs2(t,u);
}
}
int lca1(int u,int v){
if(dep1[v]>dep1[u]) swap(u,v);
int temp=dep1[u]-dep1[v];
for(int i=0;(1<<i)<=temp;i++){
if((1<<i)&temp){
u=fa1[u][i];
}
}
if(u==v) return v;
for(int i=log(n)/log(2);i>=0;i--){
if(fa1[u][i]!=fa1[v][i]){
u=fa1[u][i];
v=fa1[v][i];
}
}
return fa1[u][0];
}
int lca2(int u,int v){
if(dep2[v]>dep2[u]) swap(u,v);
int temp=dep2[u]-dep2[v];
for(int i=0;(1<<i)<=temp;i++){
if((1<<i)&temp){
u=fa2[u][i];
}
}
if(u==v) return v;
for(int i=log(n)/log(2);i>=0;i--){
if(fa2[u][i]!=fa2[v][i]){
u=fa2[u][i];
v=fa2[v][i];
}
}
return fa2[u][0];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>arr[i],brr[i]=arr[i];
for(int i=1;i<=n;i++ ) cin>>a[i];
for(int i=2;i<=n;i++) {
int t;cin>>t;
g1[t].push_back(i);
}
dfs1(1,0);
for(int i=1;i<=n;i++ ) cin>>b[i];
for(int i=2;i<=n;i++) {
int t;cin>>t;
g2[t].push_back(i);
}
dfs2(1,0);
sort(arr+1,arr+m+1,cmpA);
sort(brr+1,brr+m+1,cmpB);
int ans=0;
for(int i =1;i<=m;i++){
int be1=arr[1],en1=arr[m];
int be2=brr[1],en2=brr[m];
if(i==1) be1=arr[2];
if(i==m) en1=arr[m-1];
if(arr[i]==brr[1]) be2=brr[2];
if(arr[i]==brr[m]) en2=brr[m-1];
int ans1=lca1(be1,en1);
int ans2=lca2(be2,en2);
if(a[ans1]>b[ans2]){
ans++;
}
}
cout<<ans<<endl;
return 0;
}
原文:https://blog.nowcoder.net/n/0e09167847094e2590e861e7682ae141?f=comment
J、Journey 建图 最短路
题意:
给n个十字路口,起点和终点
蔚来每次直行、左转或在十字路口掉头,都会遇到红灯,必须等待。十字路口右转不用等红灯。
求遇到的最少红灯数量。
思路:
因为题目要求朝向,也就是说:
最终位置是 \(1->4\)(此时是向4号十字路口行驶)和最终位置是\(4->1\) 结果是不一定相同的。
具体题意看这里吧:
如果我们按十字路口为节点建图,那么我们无法判断朝向。
因此我们以单向边为节点建图,
比如说样例:
如果我们想对3->1->4建立边就是:6->2
如果我们想对4->1->3建立边就是:8->1
其实位置就是7,终止位置就是8
建完图后直接跑dj即可
代码:
#include <bits/stdc++.h>
#define ins 0x3f3f3f3f
using namespace std;
const int N = 500500;
#define pii pair<int, int>
int n, dist[N * 4 + 2];
int a[N][5];
int b[N][5];
vector<pii> g[N * 4 + 2];
int vis[N * 4 + 2];
int be, en;
void dj()
{
memset(dist, 0x3f, sizeof dist);
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, be});
dist[be] = 0;
while (q.size())
{
pii k = q.top();
q.pop();
if (vis[k.second])
continue;
vis[k.second] = 1;
for (pii t : g[k.second])
{
int dis = t.first + dist[k.second];
if (dist[t.second] > dis)
{
dist[t.second] = dis;
q.push({dis, t.second});
}
}
}
if (dist[en] >= 0x3f3f3f3f)
cout << -1 << endl;
else
cout << dist[en] << endl;
}
int get_tag(int from, int to)
{
for (int i = 1; i <= 4; ++i)
if (a[from][i] == to)
return b[from][i];
return 0;
}
void solve()
{
cin >> n;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 4; j++)
{
cin >> a[i][j];
if (a[i][j])
b[i][j] = ++cnt;
}
}
pii sta, sen;
cin >> sta.first >> sta.second >> sen.first >> sen.second;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= 4; j++)
{
if (a[i][j] == 0)
continue;
int from = get_tag( a[i][j],i);
for (int k = 1; k <= 4; ++k)
{
if (!a[i][k])
continue;
int to = get_tag( i,a[i][k]);
int w = 1;
if (k == j % 4 + 1) w = 0;
if (!from || !to)
continue;
// cout<<i<<" "<<a[i][j]<<" " <<a[i][k]<<" from " <<from<<" "<<to<<" "<<w<<endl;
g[from].push_back({w, to});
}
}
}
be=get_tag(sta.first,sta.second);
en=get_tag(sen.first,sen.second);
dj();
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
return 0;
}