"蔚来杯"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\) 结果是不一定相同的。
具体题意看这里吧:
如果我们按十字路口为节点建图,那么我们无法判断朝向。
因此我们以单向边为节点建图,
比如说样例:
image
如果我们想对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;
}
posted @ 2022-07-25 20:36  kingwzun  阅读(94)  评论(0编辑  收藏  举报