A( L C A LCA LCA) C(排序) J( 01 B F S 01BFS 01BFS)
A - Ancestor
题意
给出两棵编号 1 − n 1-n 1−n 的树 A A A 和 B B B, A A A、 B B B 树上每个节点均有一个权值。
给出 k k k 个关键点的编号 x 1 … x n x_1…x_n x1…xn。
问有多少种方案使得去掉恰好一个关键点使得剩余关键点在树 A A A 上 L C A LCA LCA的权值大于树 B B B 上 L C A LCA LCA的权值。
思路
由于 L C A LCA LCA 满足结合律,即 l c a ( a , b , c ) = l c a ( l c a ( a , b ) , l c a ( b , c ) ) lca(a,b,c)=lca(lca(a,b),lca(b,c)) lca(a,b,c)=lca(lca(a,b),lca(b,c))
预处理出关键点序列的在树A B上的前缀 L C A LCA LCA 和后缀 L C A LCA LCA
枚举去掉的关键节点,若为 i i i,则剩余点的 l c a lca lca 为 l c a ( p r e [ i − 1 ] , l a s t [ i + 1 ] ) lca(pre[i-1],last[i+1]) lca(pre[i−1],last[i+1])
#include<bits/stdc++.h>
using namespace std;
const int N = 100010, M = 2 * N;
int n, k;
int a[N], b[N], x[N];
int ha[N], ea[M], nea[M], idxa;
int hb[N], eb[M], neb[M], idxb;
int prea[N], lasta[N], preb[N], lastb[N];
int depa[N], depb[N];
int fa[N][20], fb[N][20];
queue<int> q;
void add_a(int a, int b)
{
ea[idxa] = b, nea[idxa] = ha[a], ha[a] = idxa++;
}
void add_b(int a, int b)
{
eb[idxb] = b, neb[idxb] = hb[a], hb[a] = idxb++;
}
void bfs()
{
// A
q.push(1), depa[1] = 1;
while(q.size())
{
int x = q.front();
q.pop();
for(int i = ha[x]; ~i; i = nea[i])
{
int y = ea[i];
if(depa[y]) continue;
depa[y] = depa[x] + 1;
fa[y][0] = x;
for(int j = 1; j <= 16; j++) fa[y][j] = fa[fa[y][j - 1]][j - 1];
q.push(y);
}
}
// B
q.push(1), depb[1] = 1;
while(q.size())
{
int x = q.front();
q.pop();
for(int i = hb[x]; ~i; i = neb[i])
{
int y = eb[i];
if(depb[y]) continue;
depb[y] = depb[x] + 1;
fb[y][0] = x;
for(int j = 1; j <= 16; j++) fb[y][j] = fb[fb[y][j - 1]][j - 1];
q.push(y);
}
}
}
int lca_a(int x, int y)
{
if(depa[x] > depa[y]) swap(x, y);
for(int i = 16; i >= 0; i--)
if(depa[fa[y][i]] >= depa[x]) y = fa[y][i];
if(x == y) return x;
for(int i = 16; i >= 0; i--)
if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int lca_b(int x, int y)
{
if(depb[x] > depb[y]) swap(x, y);
for(int i = 16; i >= 0; i--)
if(depb[fb[y][i]] >= depb[x]) y = fb[y][i];
if(x == y) return x;
for(int i = 16; i >= 0; i--)
if(fb[x][i] != fb[y][i]) x = fb[x][i], y = fb[y][i];
return fb[x][0];
}
void init()
{
// A
prea[1] = x[1], lasta[k] = x[k];
for(int i = 2; i <= k; i++) prea[i] = lca_a(prea[i - 1], x[i]);
for(int i = k - 1; i >= 1; i--) lasta[i] = lca_a(lasta[i + 1], x[i]);
// B
preb[1] = x[1], lastb[k] = x[k];
for(int i = 2; i <= k; i++) preb[i] = lca_b(preb[i - 1], x[i]);
for(int i = k - 1; i >= 1; i--) lastb[i] = lca_b(lastb[i + 1], x[i]);
}
void solve()
{
memset(ha, -1, sizeof ha);
memset(hb, -1, sizeof hb);
cin >> n >> k;
for(int i = 1; i <= k; i++) cin >> x[i];
int u;
// A
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 2; i <= n; i++)
{
cin >> u;
add_a(i, u), add_a(u, i);
}
// B
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = 2; i <= n; i++)
{
cin >> u;
add_b(i, u), add_b(u, i);
}
bfs();
init();
int res = 0;
for(int i = 1; i <= k; i++)
{
int wa, wb;
if(i == 1)
{
wa = a[lasta[2]];
wb = b[lastb[2]];
}else if(i == k)
{
wa = a[prea[k - 1]];
wb = b[preb[k - 1]];
}else{
wa = a[lca_a(prea[i - 1], lasta[i + 1])];
wb = b[lca_b(preb[i - 1], lastb[i + 1])];
}
// cout << wa << " " << wb << endl;
if(wa > wb) res++;
}
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
// cin >> T;
// while(T--)
{
solve();
}
return 0;
}
C - Concatenation
题意
给定 n n n 个字符串,重新排列他们,使得最后组成的字符串的字典序最小。
思路
按 a + b < b + a a+b<b+a a+b<b+a 排序
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e6 + 20;
string a[N];
bool cmp(const string &a, const string &b)
{
return a + b < b + a;
}
void solve()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i)
cin >> a[i];
sort(a, a + n, cmp);
for (int i = 0; i < n; ++i)
cout << a[i];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
{
solve();
}
return 0;
}
J - Journey
思路
双端队列, 01 b f s 01bfs 01bfs
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef pair<int,int> PII;
const int N = 500010, INF = 0x3f3f3f3f;
int n, cnt;
PII ss, tt;
PII a[N][5];
void solve()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= 4; j++)
{
cin >> a[i][j].first;
if(a[i][j].first) a[i][j].second = ++cnt;
}
}
cin >> ss.first >> ss.second >> tt.first >> tt.second;
vector<vector<PII>> g(cnt + 1, vector<PII>(0));
for(int u = 1; u <= n; u++)
{
for(int j = 1; j <= 4; j++)
{
if(!a[u][j].first) continue;
int v = a[u][j].first;
int dir;
for(int k = 1; k <= 4; k++)
{
if(u == a[v][k].first) dir = k;
}
dir = dir % 4 + 1;
for(int k = 1; k <= 4; k++)
{
if(!a[v][k].first) continue;
if(dir == k) g[a[u][j].second].push_back({a[v][k].second, 0});
else g[a[u][j].second].push_back({a[v][k].second, 1});
}
}
}
int s, t;
for(int i = 1; i <= 4; i++)
if(a[ss.first][i].first == ss.second) s = a[ss.first][i].second;
for(int i = 1; i <= 4; i++)
if(a[tt.first][i].first == tt.second) t = a[tt.first][i].second;
deque<PII> q;
vector<int> dist(cnt + 1, INF);
vector<bool> st(cnt + 1, 0);
q.push_back({s, 0});
dist[s] = 0;
while(q.size())
{
int u = q.front().first;
int d = q.front().second;
q.pop_front();
if(st[u]) continue;
st[u] = true;
dist[u] = d;
for(auto it : g[u])
{
int v = it.first, w = it.second;
if(w == 0) q.push_front({v, d});
else q.push_back({v, d + 1});
}
}
if(dist[t] > INF / 2) cout << -1 << endl;
else cout << dist[t] << endl;
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while(T--)
{
solve();
}
return 0;
}