先发一下我的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 */