“蔚来杯“2022牛客暑期多校训练营3 (A,C,J)

本文介绍了在ACM竞赛中遇到的一道关于最近公共祖先(LCA)的问题,讨论了如何处理多个关键点的LCA,并通过树剖的方法解决。作者分享了在比赛中因不熟悉LCA模板而遇到的困难,以及最终理解并解决问题的过程。同时,还提及了一道简单的字符串排序题目,以及一个涉及城市交通信号灯优化的难题,展示了在编程竞赛中快速理解和解决问题的重要性。
摘要由CSDN通过智能技术生成

A题

给出两棵编号1-n的树A和B,A、B树上每个节点均有一个权值,给出k个关键点的编号x1…xn,问有多少种方案使得去掉恰好一个关键点使得剩余关键点在树A上LCA的权值大于树B上LCA的权值。

一道基础的LCA的模板题,简单树剖求LCA但因为大家只知道2个节点的最近公共祖先,不清楚多个节点的最小公共祖先是什么,所以一直在尝试理解这个最小公共祖先是什么,所以也wa和t了好几发(虽然都是我干的,对不起队友们)后面让大佬队友理解到了最小公共祖先,就很快过了。

LCA的模板比赛的时候忘记了,现场重新写了一遍,所以耗了很多时间。

#include<bits/stdc++.h>
using namespace std;
int n,m,num,numb,head[100005],root,dep[100005],fa[100005][22],ja,jb,mi=100005,ma=-1;
int k[100005];
int qa[100005],qb[100005],headb[100005],depb[100005],fab[100005][22],xa[100005],xb[100005];
long long int ans;
bool flag;
struct op
{
  int next,to;
};
op edge[2500000];
op edgeb[2500000];
void add(int from,int to)
{  
  edge[num].next=head[from];
  edge[num].to=to;
  head[from]=num++;
}
void addb(int from,int to)
{  
  edgeb[numb].next=headb[from];
  edgeb[numb].to=to;
  headb[from]=numb++;
}
void dfs(int x,int f)
{
  dep[x]=dep[f]+1; 
  fa[x][0]=f;
  for (int i=head[x]; i!=-1; i=edge[i].next)
  {
    int e=edge[i].to; 
    if (e==f)
      continue;
    dfs(e,x);
  }
}
void dfsb(int x,int f)
{
  depb[x]=depb[f]+1; 
  fab[x][0]=f;
  for (int i=headb[x]; i!=-1; i=edgeb[i].next)
  {
    int e=edgeb[i].to; 
    if (e==f)
      continue;
    dfsb(e,x);
  }
}
int lca(int x,int y)
{
  if (dep[y]>dep[x])
    swap(x,y);
  for (int i=20; i>=0; i--)
    {
      if (dep[fa[x][i]]>=dep[y])
       x=fa[x][i];
    }
  if (x==y)
   return x;
  for (int i=20; i>=0; i--)
   {
     if (fa[x][i]!=fa[y][i])
       {
       	x=fa[x][i];
       	y=fa[y][i];
       }
   }
  return fa[x][0];
}
int lcb(int x,int y)
{
  if (depb[y]>depb[x])
    swap(x,y);
  for (int i=20; i>=0; i--)
    {
      if (depb[fab[x][i]]>=depb[y])
       x=fab[x][i];
    }
  if (x==y)
   return x;
  for (int i=20; i>=0; i--)
   {
     if (fab[x][i]!=fab[y][i])
       {
       	x=fab[x][i];
       	y=fab[y][i];
       }
   }
  return fab[x][0];
}
int main()
{
  memset(head,-1,sizeof(head));
   memset(headb,-1,sizeof(headb));
  scanf("%d%d",&n,&m);
  root=1;
  for (int i=1;i<=m;i++)
  	{
	  scanf("%d",&k[i]);
    }
    sort(k+1,k+1+m); 
  for (int i=1;i<=n;i++)
	scanf("%d",&qa[i]);
  for (int i=2; i<=n; i++ )
   {
     scanf("%d",&xa[i]);
     add(xa[i],i);
     add(i,xa[i]);
    }
   dfs(root,0); 
 //  cout<<"wdsd";
    for (int i=1; i<=20; i++ )
   for (int j=1; j<=n; j++ )
    fa[j][i]=fa[fa[j][i-1]][i-1];
//
  for (int i=1;i<=n;i++)
	scanf("%d",&qb[i]);
  for (int i=2; i<=n; i++ )
   {
     scanf("%d",&xb[i]);
     addb(xb[i],i);
     addb(i,xb[i]);
    }
   dfsb(root,0); 
 //  cout<<"wdsd";
    for (int i=1; i<=20; i++ )
   for (int j=1; j<=n; j++ )
    fab[j][i]=fab[fab[j][i-1]][i-1];
  for (int i=1;i<=m;i++)
  {
  	if (i==1)
  	{
  		ja=lca(k[2],k[m]);
  		jb=lcb(k[2],k[m]);
  		if (ja==k[i])
  		  ja=xa[k[i]];
  		if (jb==k[i])
  		  jb=xb[k[i]]; 
		if (qa[ja]>qb[jb])
		  ans++; 
		continue;
	  }
	if (i==m)
  	{
  		ja=lca(k[1],k[m-1]);
  		jb=lcb(k[1],k[m-1]);
  			if (ja==k[i])
  		  ja=xa[k[i]];
  		if (jb==k[i])
  		  jb=xb[k[i]]; 
		if (qa[ja]>qb[jb])
		  ans++; 
		continue;
	  }
	ja=lca(k[1],k[m]);
  		jb=lcb(k[1],k[m]);
  			if (ja==k[i])
  		  ja=xa[k[i]];
  		if (jb==k[i])
  		  jb=xb[k[i]]; 
		if (qa[ja]>qb[jb])
		  ans++; 
  }
  cout<<ans; 
  return 0;
}

C题

签到题了,题意是给定n个字符串,求一个将他们拼接起来的方案,使得结果的字典序最小。

很简单,就是对n个字符串排序,保障小的条件是a+b<b+a,但不知道最开始哪里出错了,导致wa了一发,然后大家重新写了一下,就过了,很离谱。

#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
 
const int N=2e6+7;
string s[N];
 
inline string read(){
	string s="";
	char ch=getchar();
	while(!(ch>='0'&&ch<='4')){
		ch=getchar();
	}
	while(ch>='0'&&ch<='4'){
		s+=ch;ch=getchar();
	}
	return s;
} 

bool cmp(int a,int b){
    return s[a]+s[b]<s[b]+s[a];
}
 
int main(){
    int n;int q[N];
    int i;
    cin>>n;
    for(i=0;i<n;i++)q[i]=i;
    for(i=0;i<n;i++)s[i]=read();
    sort(q,q+n,cmp);
    for(i=0;i<n;i++){
        cout<<s[q[i]];
    }
    cout<<endl;
}

补题时间

J题

给定一个城市有若干十字路口,右转不需要等红灯,直行、左转和掉头都需要,求起点到终点最少等几次红灯。看大佬用bfs或dij就可以过了,我们队伍坐牢3小时还是没写出来,太痛苦了。

当然也有很多大佬用spfa也可以,但我现在只会写dij了,烂透了wwww

下面是其他题解大佬的代码,我只能做到大致看懂......

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int N = 2e6 + 10;
const int M = N * 4;
typedef pair<int,int> PII;
bool st[M];
int n;
int tt;
int start,et;
int w[N][4];
int dist[M];
map<PII,int>id;
struct road{
    int a;
    int b;
}r[M];
struct edge{
    int to;
    int next;
    int w;
}tu[M];
int h[M];
int idx;
void init(){
    memset(h,-1,sizeof h);
}
void add(int u,int v,int w){//加边,从u到v,权值为w
    tu[idx].to = v;
    tu[idx].w = w;
    tu[idx].next = h[u];
    h[u] = idx ++;
}
int dij(){
    memset(dist,0x3f,sizeof dist);
    dist[start] = 0;
    priority_queue<PII,vector<PII>,greater<PII>> heap;
    heap.push({0,start});//距离;节点
    while(heap.size()){
        auto t = heap.top();
        heap.pop();
        int distance = t.first;
        int ver = t.second;
        if(st[ver]) continue;
        st[ver] = true;
        for(int i = h[ver];i != -1;i = tu[i].next){
            int j = tu[i].to;//当前的终点
            dist[j] = min(dist[j],dist[ver] + tu[i].w);
            heap.push({dist[j],j});
        }
    }
    if(dist[et] == 0x3f3f3f3f) return -1;
    else return dist[et];
}
int main(){
    init();
    tt = 1;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        for(int j = 0;j < 4;j++){
            scanf("%d",&w[i][j]);
            if(w[i][j]){
                r[tt].a = i;
                r[tt].b = w[i][j];
                id[make_pair(i,w[i][j])] = tt++;
            }
        }
    }
    for(int i = 1;i < tt;i++){
        bool a = 0;
        for(int j = 0;j < 4;j++){
            if(r[i].b && w[r[i].b][j] == r[i].a){
                a = 1;
                for(int k = 1;k <= 4;k++){
                    int h = (j + k) % 4;
                    if(k == 1 && w[r[i].b][h] != 0){
                        int t = id[{r[i].b,w[r[i].b][h]}];
                        add(i,t,0);
                    }
                    if(k != 1 && w[r[i].b][h] != 0){
                        int t = id[{r[i].b,w[r[i].b][h]}];
                        add(i,t,1);
                    }
                }
            }
            if(a) break;
        }
    }
    int s1,s2,e1,e2;
    scanf("%d%d%d%d",&s1,&s2,&e1,&e2);
    start = id[{s1,s2}];
    et = id[{e1,e2}];
    int ans = dij();
    printf("%d",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值