Gym 100851 Distance on Triangulation
题意:给你一个N边形, 然后这个n边形有n-3条边,然后询问2点之间的最短路。
题解:分治。
我们可以找到一条边,使得这幅图能分成大小相同的2幅图,那么我们就可以确定那些被分割开的询问的答案是多少了。
我们假定u v是分开的, 然后我们从u点bfs一遍现在的图,v点bfs一遍现在的图,确定所有点离这2个点的位置,对于切断的询问更新答案。
需要注意的就是,每次都一定要重新建图,免得遍历太多的没有用的边。
递归结束的时候是这幅图只有3个点了,如果在3个点中的最短路,那么一定是1,假定2点不重合。
代码1:离线写法
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("distance.in","r",stdin); freopen("distance.out","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; struct Node{ int u, v, id; }; vector<int> vc[N][2], ee[N]; vector<pll> e[N][2]; vector<Node> q[N][2]; int ans[N], pos[N], num[N]; int lf[N], rt[N], ok[N]; int d[N][2]; queue<int> que; void bfs(int b, int id){ d[b][id] = 0; que.push(b); while(!que.empty()){ int u = que.front(); que.pop(); for(auto v : ee[u]){ if(d[v][id] > d[u][id]+1){ d[v][id] = d[u][id]+1; que.push(v); } } } } void solve(int k, int op){ if(vc[k][op].size() <= 3 || q[k][op].size() == 0) return ; int nodes = vc[k][op].size(); for(int i = 0; i < nodes; ++i){ ee[i].clear(); pos[vc[k][op][i]] = i; d[i][0] = d[i][1] = inf; ee[i].pb((i+1)%nodes); ee[i].pb((i-1+nodes)%nodes); } int Max = inf, id = 0; for(int i = 0; i < e[k][op].size(); ++i){ int u = e[k][op][i].fi, v = e[k][op][i].se; int tmp = max(pos[v]-pos[u], nodes - pos[v]+pos[u]); if(tmp < Max) { Max = tmp; id = i; } ee[pos[u]].pb( pos[v]); ee[pos[v]].pb( pos[u]); } vc[k+1][0].clear(); vc[k+1][1].clear(); q[k+1][1].clear(); q[k+1][0].clear(); e[k+1][0].clear(); e[k+1][1].clear(); int tu = e[k][op][id].fi, tv = e[k][op][id].se; for(int i = 0; i < nodes; ++i){ if(vc[k][op][i] == tu || vc[k][op][i] == tv){ vc[k+1][0].pb(vc[k][op][i]); vc[k+1][1].pb(vc[k][op][i]); } else if(vc[k][op][i] < tu || vc[k][op][i] > tv) vc[k+1][0].pb(vc[k][op][i]), lf[vc[k][op][i]] = 1; else vc[k+1][1].pb(vc[k][op][i]), rt[vc[k][op][i]] = 1; } bfs(pos[tu], 0); bfs(pos[tv], 1); for(int i = 0, u, v; i < e[k][op].size(); ++i){ u = e[k][op][i].fi, v = e[k][op][i].se; if(lf[u] || lf[v]) e[k+1][0].pb(e[k][op][i]); else if(rt[u] || rt[v]) e[k+1][1].pb(e[k][op][i]); } for(int i = 0, u, v, aid; i < q[k][op].size(); ++i){ u = q[k][op][i].u, v = q[k][op][i].v, aid = q[k][op][i].id; if(lf[u] && lf[v]) { q[k+1][0].pb(q[k][op][i]); continue; } else if(rt[u] && rt[v]){ q[k+1][1].pb(q[k][op][i]); continue; } u = pos[u], v = pos[v]; ans[aid] = min(d[u][1]+d[v][1], d[u][0] + d[v][0]); } for(auto v : vc[k][op]){ lf[v] = rt[v] = 0; ok[v] = 0; } solve(k+1, 0); solve(k+1, 1); } int main(){ Fopen; int n, m, u, v; scanf("%d", &n); for(int i = 1; i <= n; ++i){ vc[0][0].pb(i); } for(int i = 1; i <= n - 3; ++i){ scanf("%d%d", &u, &v); if(u > v) swap(u,v); e[0][0].pb({u,v}); } scanf("%d", &m); for(int i = 1; i <= m; ++i){ scanf("%d%d", &u, &v); if(u > v) swap(u,v); if(u == v) continue; q[0][0].pb({u,v,i}); } solve(0,0); for(int i = 1; i <= m; ++i){ printf("%d\n", ans[i]); } return 0; }
离线看着麻烦 其实还是挺简单的。
代码2:在线写法
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("distance.in","r",stdin); freopen("distance.out","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 5e4+100; vector<int> ee[N]; int pos[N], que[N]; struct Node{ vector<int> v, d[2]; vector<pll> e; int mn, mx, nodes; Node * lson, * rson; void bfs(int b,int id){ d[id][b] = 0; int l = 0, r = 1; que[l] = b; while(l < r){ int u = que[l++]; for(auto v : ee[u]){ if(d[id][v] > d[id][u]+1){ d[id][v] = d[id][u]+1; que[r++] = v; } } } } void solve(){ nodes = v.size(); if(nodes <= 3) return ; for(int i = 0; i < nodes; ++i){ pos[v[i]] = i; d[0].pb(inf); d[1].pb(inf); ee[i].clear(); ee[i].pb((i+1)%nodes); ee[i].pb((i+nodes-1)%nodes); } int Max = inf, id = 0; for(int i = 0; i < nodes-3; ++i){\ int x = pos[e[i].fi], y = pos[e[i].se]; int tmp = max(y-x, nodes-(y-x)); if(tmp < Max) { Max = tmp; id = i; } ee[x].pb(y); ee[y].pb(x); } mn = e[id].fi, mx = e[id].se; lson = new Node; rson = new Node; for(int i = 0; i < nodes; ++i){ if(v[i] >= mn && v[i] <= mx) lson -> v.pb(v[i]); if(v[i] <= mn || v[i] >= mx) rson -> v.pb(v[i]); } bfs(pos[mn], 0); bfs(pos[mx], 1); for(int i = 0; i < nodes-3; ++i){ int x = e[i].fi, y = e[i].se; if((x > mn && x < mx) || (y > mn && y < mx)) lson -> e.pb(e[i]); if(x < mn || y < mn || x > mx || y > mx) rson -> e.pb(e[i]); } lson -> solve(); rson -> solve(); } int Query(int x, int y){ if(nodes <= 3) return 1; if((x > mn && x < mx) && (y > mn && y < mx)) return lson -> Query(x,y); if((x < mn || x > mx) && (y < mn || y > mx)) return rson -> Query(x,y); x = lower_bound(v.begin(), v.end(), x) - v.begin(); y = lower_bound(v.begin(), v.end(), y) - v.begin(); return min(d[0][x]+d[0][y], d[1][x]+d[1][y]); } }rt; int main(){ Fopen; int n, m, u, v; scanf("%d", &n); for(int i = 1; i <= n; ++i){ rt.v.pb(i); } for(int i = 1; i <= n - 3; ++i){ scanf("%d%d", &u, &v); if(u > v) swap(u,v); rt.e.pb({u,v}); } rt.solve(); scanf("%d", &m); while(m--){ scanf("%d%d", &u, &v); int ans = 0; if(u != v) ans = rt.Query(u,v); printf("%d\n", ans); } return 0; }
在线写法要确定询问的点是不是都在一副新的图上,是就继续往下递归,否就在这个地方询问。
这题目中在线比离线的慢一点点。
因为在线写法的询问是严格的lg(n)是一个固定值,因为每次往下走,你就会使得二分的长度/2,就相当于是二分了一次了。。。。。
然后我又写了一个bug,疯狂TLE,惊了。
int Max = inf, id = 0; for(int i = 0; i < e[k][op].size(); ++i){ int u = e[k][op][i].fi, v = e[k][op][i].se; int tmp = max(pos[v]-pos[u], nodes - pos[v]+pos[u]); if(tmp < Max) { tmp = Max; id = i; } ee[pos[u]].pb( pos[v]); ee[pos[v]].pb( pos[u]); }
看着没什么问题。。。实际上问题大了。
应该是 Max = tmp。。。 这样加多了递归的层数,然后会加多存的东西,会导致TLE和MLE。
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("distance.in","r",stdin); freopen("distance.out","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL mod = (int)1e9+7; const int N = 1e5 + 100; struct Node{ int u, v, id; }; vector<int> vc[N][2], ee[N]; vector<pll> e[N][2]; vector<Node> q[N][2]; int ans[N], pos[N], num[N]; int lf[N], rt[N], ok[N]; int d[N][2]; queue<int> que; void bfs(int b, int id){ d[b][id] = 0; que.push(b); while(!que.empty()){ int u = que.front(); que.pop(); for(auto v : ee[u]){ if(ok[v] && d[v][id] > d[u][id]+1){ d[v][id] = d[u][id]+1; que.push(v); } } } } void solve(int k, int op){ if(vc[k][op].size() <= 3 || q[k][op].size() == 0) return ; for(int i = 0; i < vc[k][op].size(); ++i){ pos[vc[k][op][i]] = i; d[vc[k][op][i]][0] = d[vc[k][op][i]][1] = inf; ok[vc[k][op][i]] = 1; } int Max = inf, id = 0, totnum = vc[k][op].size(); for(int i = 0; i < e[k][op].size(); ++i){ int u = e[k][op][i].fi, v = e[k][op][i].se; int tmp = max(pos[v]-pos[u]-1, totnum - (pos[v]-pos[u]-1) - 2); if(tmp < Max) { tmp = Max; id = i; } } vc[k+1][0].clear(); vc[k+1][1].clear(); vc[k+1][0].shrink_to_fit(); vc[k+1][1].shrink_to_fit(); q[k+1][1].clear(); q[k+1][0].clear(); q[k+1][1].shrink_to_fit(); q[k+1][0].shrink_to_fit(); e[k+1][0].clear();e[k+1][1].clear(); e[k+1][0].shrink_to_fit(); e[k+1][1].shrink_to_fit(); int tu = e[k][op][id].fi, tv = e[k][op][id].se; for(int i = 0; i < totnum; ++i){ if(vc[k][op][i] == tu || vc[k][op][i] == tv){ vc[k+1][0].pb(vc[k][op][i]); vc[k+1][1].pb(vc[k][op][i]); } else if(vc[k][op][i] < tu || vc[k][op][i] > tv) vc[k+1][0].pb(vc[k][op][i]), lf[vc[k][op][i]] = 1; else vc[k+1][1].pb(vc[k][op][i]), rt[vc[k][op][i]] = 1; } bfs(tu,0); bfs(tv,1); for(int i = 0, u, v; i < e[k][op].size(); ++i){ u = e[k][op][i].fi, v = e[k][op][i].se; if(lf[u] || lf[v]) e[k+1][0].pb(e[k][op][i]); else if(rt[u] || rt[v]) e[k+1][1].pb(e[k][op][i]); } for(int i = 0, u, v, aid; i < q[k][op].size(); ++i){ u = q[k][op][i].u, v = q[k][op][i].v, aid = q[k][op][i].id; if(lf[u] && lf[v]) { q[k+1][0].pb(q[k][op][i]); continue; } else if(rt[u] && rt[v]){ q[k+1][1].pb(q[k][op][i]); continue; } ans[aid] = min(ans[aid], d[u][0]+d[v][0]); ans[aid] = min(ans[aid], d[u][1]+d[v][1]); } for(auto v : vc[k][op]){ lf[v] = rt[v] = 0; ok[v] = 0; } vc[k][op].clear(); vc[k][op].shrink_to_fit(); e[k][op].clear(); e[k][op].shrink_to_fit(); q[k][op].clear(); q[k][op].shrink_to_fit(); solve(k+1, 0); solve(k+1, 1); } int main(){ Fopen; int n, m, u, v; scanf("%d", &n); for(int i = 1; i < n; ++i){ vc[0][0].pb(i); ee[i].pb(i+1); ee[i+1].pb(i); } vc[0][0].pb(n); ee[1].pb(n); ee[n].pb(1); for(int i = 1; i <= n - 3; ++i){ scanf("%d%d", &u, &v); if(u > v) swap(u,v); e[0][0].pb({u,v}); ee[u].pb(v); ee[v].pb(u); } scanf("%d", &m); memset(ans, inf, sizeof ans); for(int i = 1; i <= m; ++i){ scanf("%d%d", &u, &v); if(u > v) swap(u,v); if(u == v) { ans[i] = 0; continue; } q[0][0].pb({u,v,i}); } solve(0,0); for(int i = 1; i <= m; ++i){ printf("%d\n", ans[i]); } return 0; }
然后顶着这个破代码 各种乱写 各种MLE, 然后把每一层的东西不需要的那时候马上clear 和 清除空间, 然后就过了。
然后想改成在线的,改了半天改不动才发现了上面的那个问题。