"蔚来杯"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个十字路口,起点和终点
蔚来每次直行、左转或在十字路口掉头,都会遇到红灯,必须等待。十字路口右转不用等红灯。
求遇到的最少红灯数量。
思路:
因为题目要求朝向,也就是说:
最终位置是 (此时是向4号十字路口行驶)和最终位置是 结果是不一定相同的。
具体题意看这里吧:
如果我们按十字路口为节点建图,那么我们无法判断朝向。
因此我们以单向边为节点建图,
比如说样例:
如果我们想对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; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16518746.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步