关于中间鸽的那一段时间是因为在打USACO和XMOJ不好发。。。
然后也不是很想去补了。。。
昨天上了kruskal重构树,今天下午写了两题。(快写自闭了)。
稍微再复盘下kruskal重构树的性质:
对于图上任意两个点中的所有简单路径上的最大边权的最小值=最小生成树上这两个点的路径上的最大边权(稍微想想基本可以证明了)=这两个点在重构树上的lca。(这是最主要的性质)
T1.水灾
原题在牛客网上。
基本上是模板了,注意一下建边要从大到小。
具体咋做看眼昨天的预习。
code:
#include<bits/stdc++.h>
// #define int long long
#define debug puts("hwl");
using namespace std;
const int N = 1e6 + 10, M = 5e5 + 10;
int fa[N], a[N];
vector <int> v[N];
int dfn[N], dep[N], cnt;
int rt[N][21], qes[N];
struct Edge{
int u, v, w;
}e[M];
int find(int x){
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y){
x = find(x), y = find(y);
v[x].push_back(y);
v[y].push_back(x);
fa[x] = y;
return;
}
bool cmp(Edge a, Edge b){
return a.w > b.w;
}
int tot;
void kruskal(int n, int m){
tot = n;
for(int i = 1; i <= m; i++){
int x = e[i].u, y = e[i].v, w = e[i].w;
if(find(x) == find(y)) continue;
else tot++;
a[tot] = w;
merge(x, tot);
merge(y, tot);
}
return;
}
int n, m, q;
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
rt[x][0] = fa;
// cout << fa << " " << x << endl;
if(x <= n)
dfn[x] = ++cnt;
for(int i = 1; i <= 20; i++)
rt[x][i] = rt[rt[x][i - 1]][i - 1];
for(int y : v[x]){
if(y == fa) continue;
dfs(y, x);
}
return;
}
bool cmp1(int a, int b){
return dfn[a] < dfn[b];
}
int LCA(int x, int y){
if(dep[x] > dep[y]) swap(x, y);
for(int tmp = dep[y] - dep[x], j = 0; tmp; j++, tmp >>= 1){
if(tmp & 1) y = rt[y][j];
}
// cout << x << " " << y << endl;
if(x == y) return x;
for(int i = 20; i >= 0; i--){
if(rt[x][i] != rt[y][i]){
x = rt[x][i];
y = rt[y][i];
}
}
return rt[x][0];
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> q;
for(int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
sort(e + 1, e + 1 + m, cmp);
for(int i = 1; i <= 2 * n; i++) fa[i] = i;
kruskal(n, m);
for(int i = 1; i <= tot; i++){
if(v[i].size() == 2 || v[i].size() == 0)
dfs(i, i);
}
// for(int i = 1; i <= tot; i++){
// cout << dfn[i] << " " << i << endl;
// }
int last_ans = 0;
// cout << LCA(4, 1) << endl;
while(q--){
int k;
cin >> k;
for(int i = 1; i <= k; i++){
cin >> qes[i];
qes[i] = qes[i] ^ last_ans;
}
sort(qes + 1, qes + 1 + k, cmp1);
int ans = -0x3f3f3f3f;
for(int i = 1; i < k; i++){
int lca = LCA(qes[i], qes[i + 1]);
// cout << qes[i] << " " << qes[i + 1] << " " << lca << endl;
ans = max(ans, a[lca]);
qes[i] = 0;
}
qes[k] = 0;
if(ans == -0x3f3f3f3f) ans = 0;
cout << ans << endl;
last_ans = ans;
}
return 0;
}//by hwl
/*
6 7 2
1 2 1
2 3 2
3 4 4
4 5 3
2 5 7
5 6 5
1 6 6
3 1 3 5
2 4 1
*/
T2.Qpwoeirut and Vertices
解决下昨天的问题。
对于一颗有根树,求编号在某一段区间的点的最浅的LCA,可以只算这段区间dfn最大和最小的两个点的lca。
至于为啥。下面给出草率的证明。
假设点x,y,z dfn[x]<dfn[y]<dfn[z].
那么z有两种可能。
1.z在y的子树中,那么此时x,y和x,z的lca相等。
2.z不在y的子树中,那么此时x,z的lca比x,y的lca浅。
那么我们就可以用线段树来维护dfn最大和最小的那两个点。
具体的做法不在赘述,见 2024.3.24 课件预习
code:
#include<bits/stdc++.h>
//#define int long long
#define debug puts("hwl");
using namespace std;
const int N = 2e5 + 10;
int fa[N], a[N];
vector <int> v[N];
int dfn[N], dep[N], rt[N][23];
struct Edge{
int u, v;
}e[N];
struct Tree{
int maxn, minn;
}tree[N << 2];
int find(int x){
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y){
x = find(x), y = find(y);
v[x].push_back(y);
v[y].push_back(x);
fa[x] = y;
return;
}
int tot;
void kruskal(int n, int m){
tot = n;
for(int i = 1; i <= m; i++){
int x = e[i].u, y = e[i].v, w = i;
if(find(x) == find(y)) continue;
else tot++;
a[tot] = w;
merge(x, tot);
merge(y, tot);
}
return;
}
int n, m, q, cnt;
void dfs(int x, int fa){
dep[x] = dep[fa] + 1;
rt[x][0] = fa;
if(x <= n)
dfn[x] = ++cnt;
for(int i = 1; i <= 20; i++)
rt[x][i] = rt[rt[x][i - 1]][i - 1];
for(int y : v[x]){
if(y == fa) continue;
dfs(y, x);
}
return;
}
int LCA(int x, int y){
if(dep[x] > dep[y]) swap(x, y);
for(int tmp = dep[y] - dep[x], j = 0; tmp; j++, tmp >>= 1){
if(tmp & 1) y = rt[y][j];
}
// cout << x << " " << y << endl;
if(x == y) return x;
for(int i = 20; i >= 0; i--){
if(rt[x][i] != rt[y][i]){
x = rt[x][i];
y = rt[y][i];
}
}
return rt[x][0];
}
void pushup(int op){
int ls = (op << 1), rs = (op << 1) + 1;
if(dfn[tree[ls].maxn] > dfn[tree[rs].maxn]) tree[op].maxn = tree[ls].maxn;
else tree[op].maxn = tree[rs].maxn;
if(dfn[tree[ls].minn] < dfn[tree[rs].minn]) tree[op].minn = tree[ls].minn;
else tree[op].minn = tree[rs].minn;
return;
}
void build(int l, int r, int op){
if(l == r){
tree[op].maxn = l;
tree[op].minn = l;
return;
}
int mid = (l + r) >> 1;
build(l, mid, op << 1);
build(mid + 1, r, (op << 1) + 1);
pushup(op);
return;
}
int query1(int x, int y, int l, int r, int op){
int res = 0, minn = 0x3f3f3f3f;
if(x <= l && r <= y){
return tree[op].minn;
}
int mid = (l + r) >> 1;
if(x <= mid){
int idx = query1(x, y, l, mid, op << 1);
if(minn >= dfn[idx]){
res = idx;
minn = dfn[idx];
}
}
if(y >= mid + 1){
int idx = query1(x, y, mid + 1, r, (op << 1) + 1);
if(minn >= dfn[idx]){
res = idx;
minn = dfn[idx];
}
}
return res;
}
int query2(int x, int y, int l, int r, int op){
int res = 0, maxn = -0x3f3f3f3f;
if(x <= l && r <= y){
return tree[op].maxn;
}
int mid = (l + r) >> 1;
if(x <= mid){
int idx = query2(x, y, l, mid, op << 1);
if(maxn <= dfn[idx]){
res = idx;
maxn = dfn[idx];
}
}
if(y >= mid + 1){
int idx = query2(x, y, mid + 1, r, (op << 1) + 1);
if(maxn <= dfn[idx]){
res = idx;
maxn = dfn[idx];
}
}
return res;
}
int main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--){
cin >> n >> m >> q;
for(int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v;
for(int i = 1; i <= 2 * n; i++) fa[i] = i;
kruskal(n, m);
for(int i = 1; i <= tot; i++){
if(v[i].size() == 2 || v[i].size() == 0){
cnt = 0;
dfs(i, i);
}
}
// cout << endl;
// for(int i = 1; i <= tot; id++) cout << dfn[i] << " " << i << endl;
// cout << endl;
build(1, n, 1);
while(q--){
int li, ri, x, y;
cin >> li >> ri;
x = query1(li, ri, 1, n, 1);//min
y = query2(li, ri, 1, n, 1);//max
// cout << x << " " << y << endl;
// cout << endl;
int lca = LCA(x, y);
// cout << lca << endl;
cout << a[lca] << " ";
}
cout << endl;
for(int i = 1; i <= 2 * n; i++){
v[i].clear();
dfn[i] = dep[i] = 0;
a[i] = 0;
for(int j = 0; j <= 20; j++) rt[i][j] = 0;
}
}
return 0;
}//by hwl
/*
1
5 5 5
1 2
1 3
2 4
3 4
3 5
1 4
3 4
2 2
2 5
3 5
*/
晚上vp了一场cf。
回去睡觉咯。