良心送分题【牛客挑战赛35 E】【LCA处理+最短路】

良心送分题

http://www.nowcoder.com/questionTerminal/3e7c602daeab47cf812929accce4cd43

先发一下我的CSDN博客哟:https://blog.csdn.net/qq_41730082/article/details/103643159
首先,这道题的思维展开,肯定不能把所有的点都用进来,那么,选择的点,我们可以只考虑起点和终点还有特殊的像M条链接边的点了,所以,点数的上限就是1e5,而不是2e10了,这就是对于点的优化了。

再者,比赛的时候我处理这1e5个点的时候用了两两平方的方式来求LCA,尝试着卡数据随机……但是很显然,这次的数据造的很用心,这样的做法确实被卡掉了,于是就是想办法去优化这个求LCA的过程。

我们可以知道,最后的时候1~K棵树都会分布着一些点,那么的话,我们就是要去对点进行操作即可,首先,当仁不让的就是1号根结点,如有必要是一定要放进来的。其次再看,我们可以把一些打了标记(也就是M条边中用到的点给放进来处理),我这里用了他们的dfs序来进行维护。

说到为什么要用dfs序,我的目的是为了保证它们尽可能的是在一条链上,或者就是链之间的转换了,所以就保证了点的数量最多是在1e5的基础上翻倍而已。

按照dfs序我们有序的进行操作,首先从根结点开始,我们可以知道的是,如果在同一条链上的时候,我们就可以直接增加了,因为它的深度一定是更浅的并且它的LCA一定是到更浅的两个之一的点。如果在两个不同的链上的时候,我们肯定会找到它们的LCA,这时候这时候,我们判断它与前两个的关系,看是否可以继续往上缩这样的过程。这块处理的时间复杂度是O(M*log(N))。

然后,我们已经把点和边都处理出来了,从起点开始像终点跑Dijkstra即可,此处的时间复杂度是O(N*log(N)),所以总的时间复杂度就满足条件了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define _ABS(x, y) ( x > y ? (x - y) : (y - x) )
#define lowbit(x) ( x&( -x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define efs 1e-7
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
//const int maxN = 1e6 + 7;
int N, M, K, head[200005], cnt, S_beg, T_end;
struct Tree_Eddge
{
    int nex, to;
    Tree_Eddge(int a=-1, int b=0):nex(a), to(b) {}
}Tree_edge[400005];
inline void Tree_addEddge(int u, int v)
{
    Tree_edge[cnt] = Tree_Eddge(head[u], v);
    head[u] = cnt++;
}
inline void Tree_add(int u, int v) { Tree_addEddge(u, v); Tree_addEddge(v, u); }
int dfn[200005], _Index, root[200005][19], deep[200005];
//LCA部分
void LCA_dfs(int u, int fa)
{
    root[u][0] = fa;
    deep[u] = deep[fa] + 1;
    dfn[u] = ++_Index;
    for(int i=0; i<log2(1. * N); i++) { root[u][i + 1] = root[root[u][i]][i]; }
    for(int i=head[u], v; ~i; i=Tree_edge[i].nex)
    {
        v = Tree_edge[i].to;
        if(v == fa) continue;
        LCA_dfs(v, u);
    }
}
inline int _LCA(int u, int v)
{
    if(deep[u] < deep[v]) swap(u, v);
    int det = deep[u] - deep[v];
    for(int i=log2(det); i>=0; i--)
    {
        if((det >> i) & 1) u = root[u][i];
    }
    if(u == v) return u;
    for(int i=log2(N); i>=0; i--)
    {
        if(root[u][i] ^ root[v][i])
        {
            u = root[u][i];
            v = root[v][i];
        }
    }
    return root[u][0];
}
//建立新图部分,保证点数量
map<int, int> V[50004];
map<int, int>::iterator it;
int new_id;
inline int Hash_map_id(int k_id, int t_id)
{
    if(!V[k_id][t_id]) V[k_id][t_id] = ++new_id;
    return V[k_id][t_id];
}
int new_head[500005], new_cnt;
struct new_Eddge
{
    int nex, to, val;
    new_Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), val(c) {}
}new_edge[1000006];
inline void new_addEddge(int u, int v, int w)
{
    new_edge[new_cnt] = new_Eddge(new_head[u], v, w);
    new_head[u] = new_cnt++;
}
inline void new_add(int u, int v, int w) { new_addEddge(u, v, w); new_addEddge(v, u, w); }
pair<int, int> a[100005], que[100005];
int a_num, top;
inline bool cmp(pair<int, int> e1, pair<int, int> e2) { return dfn[e1.first] < dfn[e2.first]; }
inline void Insert_Point(int kid, pair<int, int> x) //开始放点进去了
{
    if(top < 2)
    {
        que[++top] = x;
        return;
    }
    int lca = _LCA(que[top].first, x.first);
    while(top > 1 && dfn[lca] <= dfn[que[top - 1].first])
    {
        new_add(que[top].second, que[top - 1].second, deep[que[top].first] - deep[que[top - 1].first]);
        top--;
    }
    if((lca ^ que[top].first) && top > 1)
    {
        new_add(que[top].second, Hash_map_id(kid, lca), deep[que[top].first] - deep[lca]);
        que[top] = MP(lca, Hash_map_id(kid, lca));
    }
    que[++top] = x;
}
inline void new_Graph_Build()   //建立新图过程
{
    for(int ki=1; ki<=K; ki++) //有K棵子树
    {
        a_num = top = 0;
        Hash_map_id(ki, 1);
        for(it = V[ki].begin(); it != V[ki].end(); it++)
        {
            a[++a_num] = *it;
        }
        sort(a + 1, a + a_num + 1, cmp);
        for(int i=1; i<=a_num; i++) Insert_Point(ki, a[i]);
        while(top > 1)
        {
            new_add(que[top].second, que[top - 1].second, deep[que[top].first] - deep[que[top - 1].first]);
            top --;
        }
    }
}
//Dijkstra跑最短路
struct node
{
    int id; ll val;
    node(int a = 0, ll b = 0):id(a), val(b) {}
    friend bool operator < (node e1, node e2) { return e1.val > e2.val; }
};
ll dis[500006];
inline void Dijkstra()
{
    for(int i=1; i<=new_id; i++) dis[i] = INF;
    dis[S_beg] = 0;
    priority_queue<node> Q;
    Q.push(node(S_beg, 0));
    node now;
    while(!Q.empty())
    {
        now = Q.top(); Q.pop();
        int u = now.id;
        if(now.val > dis[u]) continue;
        for(int i=new_head[u], v; ~i; i=new_edge[i].nex)
        {
            v = new_edge[i].to; ll w = new_edge[i].val;
            if(dis[v] > dis[u] + w)
            {
                dis[v] = dis[u] + w;
                Q.push(node(v, dis[v]));
            }
        }
    }
    printf("%lld\n", dis[T_end] < INF ? dis[T_end] : -1);
}
inline void init()
{
    cnt = new_cnt = new_id = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
    memset(new_head, -1, sizeof(new_head));
}
int main()
{
    scanf("%d%d%d", &N, &M, &K);
    init();
    for(int i=1, u, v; i<N; i++)    //建立初始化树
    {
        scanf("%d%d", &u, &v);
        Tree_add(u, v);
    }
    LCA_dfs(1, 0);
    int k1, t1, k2, t2;
    for(int i=1; i<=M; i++)
    {
        scanf("%d%d%d%d", &k1, &t1, &k2, &t2);
        new_add(Hash_map_id(k1, t1), Hash_map_id(k2, t2), 1);
    }
    scanf("%d%d%d%d", &k1, &t1, &k2, &t2);
    S_beg = Hash_map_id(k1, t1); T_end = Hash_map_id(k2, t2);
    new_Graph_Build();
    Dijkstra();
    return 0;
}
/*
12 3 1
1 2
2 3
2 4
2 5
3 6
3 7
3 8
8 9
9 10
9 11
10 12
1 5 1 8
1 4 1 10
1 11 1 12
1 1 1 12
*/
全部评论

相关推荐

咦哟,从去年八月份开始长跑,两处实习转正都失败了,风雨飘摇,终于拿到offer了更新一下面试记录:秋招:多部门反复面试然后挂掉然后复活,具体问了啥已经忘了,只是被反复煎炸,直至焦香😋春招:base北京抖音hr打来电话说再次复活,准备面试,gogogo北京抖音一面:六道笔试题:1.promise顺序2.定义域问题3.flat展开4.并发请求5.岛屿数量算法(力扣)深度,广度都写6.忘记了,好像也是算法,难度中等其他问题多是框架底层设计,实习项目重难点~~~秒过😇北京抖音二面:三道笔试题:(为什么只有三道是因为第三道没做出来,卡住了)1.中等难度算法(忘记啥题了,应该是个数组的)2.认识js的继承本质(手写继承模式,深入js的面相对象开发)3.手写vue的响应式(卡在了watch,导致挂掉)---后知后觉是我的注册副作用函数写得有问题,有点紧张了其他题目多是项目拷打,项目亮点,对实习项目的贡献~~~第二天,挂,but立马复活转战深圳客服当天约面深圳客服一面:六道笔试题,由于面过太多次字节,面试官叫我直接写,不用讲,快些写完😋,具体都是些继承,深拷贝(注意对数组对象分开处理,深层次对象,循环引用),加中等难度算法题~~~秒过深圳客服二面:口诉八股大战:大概囊括网络,浏览器渲染原理,动画优化,时间循环,任务队列等等(你能想到的简单八股通通拉出来鞭尸😋)算法题:笔试题6道:1:找出数组内重复的数,arr[0]-arr[n]内的数大小为[1-n],例如[1,2,2,3,3]返回[2,3],要求o(n),且不使用任何额外空间(做到了o(n),空间方面欠佳,给面试官说进入下一题,做不来了)2:原滋原味的继承(所以继承真滴很重要)3:力扣股票购买时机难度中等其他滴也忘记了,因为拿到offer后鼠鼠一下子就落地了,脑子自动过滤掉可能会攻击鼠鼠的记忆😷~~~秒过深圳客服三面:项目大战参与战斗的人员有:成员1:表单封装及其底层原理,使用成本的优化,声明式表单成员2:公司内部库生命周期管理成员3:第三方库和内部库冲突如何源码断点调试并打补丁解决成员4:埋点的艺术成员5:线上项目捷报频传如何查出内鬼成员6:大文件分片的风流趣事成员7:设计模式对对碰成员8:我构建hooks应对经理的新增的小需求的故事可能项目回答的比较流利,笔试题3道,都很简单,相信大家应该都可以手拿把掐😇~~~过过过无hr面后续煎熬等待几天直接hr打电话发offer了,希望大家也可以拿到自己心仪的offer
法力无边年:牛哇,你真是准备得充分,我对你没有嫉妒,都是实打实付出
查看19道真题和解析
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务