传送门
(这题居然现场写出来了 )
题意
小 Z 喜欢最小生成树。
小 Z 有一张 nn 个点 mm 条边的图,每条边连接点
u
i
,
v
i
u_i,v_i
ui,vi,边权为
w
i
w_i
wi。他想进行
q
q
q 次操作,有如下两种类型:
1.修改第
x
x
x 条边为连接点
y
,
z
y,z
y,z ,边权为
t
t
t ;
2.查询只用编号在
[
l
,
r
]
[l,r]
[l,r]范围内的边,得到的最小生成树权值是多少。
对于每次询问,输出最小生成树权值。如果无解,输出 Impossible 。
1
≤
n
≤
200
,
1
≤
m
≤
30000
,
1
≤
q
≤
30000
,
1
≤
u
i
,
v
i
≤
n
,
1
≤
w
i
≤
100000
。
1≤n≤200,1≤m≤30000,1≤q≤30000,1≤u_i ,v_i≤n,1≤w_i≤100000。
1≤n≤200,1≤m≤30000,1≤q≤30000,1≤ui,vi≤n,1≤wi≤100000。
1
≤
x
≤
m
,
1
≤
y
,
z
≤
n
,
1
≤
t
≤
100000
。
1\le x\le m,1\le y,z\le n,1\le t\le 100000。
1≤x≤m,1≤y,z≤n,1≤t≤100000。
分析
区间问题,优先考虑线段树(虽然这题暴力也能过 )
然后就能想到,线段树区间维护最小生成树所用到的边,是可以进行pushup的。
pushup操作具体为对两个儿子维护的边集做Kruskal,把结果存进父节点的边集中。
而且这颗线段树是单点更新的,不需要维护懒标记~
核心代码:
struct edges
{
int u, v, w;
} edge[M];
struct node
{
int l, r;
vector<int> seq;//边集:最小生成树所用到的边的编号(限制在[l~r]中)
} tr[M << 2];
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
bool Union(int a, int b)
{
a = find(a), b = find(b);
if (a == b)
return false;
fa[a] = b;
return true; //成功进行了一次合并
}
void pushup(vector<int> &s1, vector<int> &s2, vector<int> &s3) //很厉害的合并操作
{
static vector<int> tmp; //归并排序进行Kruskal,看大佬代码学的
init(); //初始化并查集数组 fa[i]=i
tmp.clear();
int i = 0, j = 0;
while (i < s2.size() || j < s3.size())
{
if (j >= s3.size() || (i < s2.size() && edge[s2[i]].w < edge[s3[j]].w))
{
if (Union(edge[s2[i]].u, edge[s2[i]].v)) //如果Union操作成功进行了一次合并
tmp.push_back(s2[i]);
i++;
}
else
{
if (Union(edge[s3[j]].u, edge[s3[j]].v)) //如果Union操作成功进行了一次合并
tmp.push_back(s3[j]);
j++;
}
}
s1 = tmp;
}
代码
#include <bits/stdc++.h>
using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)
//---------------
const int N = 210, M = 3e4 + 10;
int n, m, q, u, v, w;
int fa[N];
vector<int> ans;
void init()
{
fir(i, 1, n) fa[i] = i;
}
struct edges
{
int u, v, w;
} edge[M];
struct node
{
int l, r;
vector<int> seq;
} tr[M << 2];
int find(int x)
{
if (x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
bool Union(int a, int b)
{
a = find(a), b = find(b);
if (a == b)
return false;
fa[a] = b;
return true; //成功进行了一次合并
}
void pushup(vector<int> &s1, vector<int> &s2, vector<int> &s3) //很厉害的合并操作
{
static vector<int> tmp; //归并排序进行Kruskal
init(); //初始化并查集数组
tmp.clear();
int i = 0, j = 0;
while (i < s2.size() || j < s3.size())
{
if (j >= s3.size() || (i < s2.size() && edge[s2[i]].w < edge[s3[j]].w))
{
if (Union(edge[s2[i]].u, edge[s2[i]].v)) //如果Union操作成功进行了一次合并
tmp.push_back(s2[i]);
i++;
}
else
{
if (Union(edge[s3[j]].u, edge[s3[j]].v)) //如果Union操作成功进行了一次合并
tmp.push_back(s3[j]);
j++;
}
}
s1 = tmp;
}
void pushup(int u)
{
pushup(tr[u].seq, tr[u << 1].seq, tr[u << 1 | 1].seq);
}
void build(int u, int l, int r)
{
tr[u].l = l, tr[u].r = r;
if (l == r)
{
tr[u].seq.push_back(l); //节点中维护最小生成树的边(分析可知,父节点的信息可由子节点推出)
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u); //合并操作操作
}
void modify(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r)
{
tr[u].seq.clear();
tr[u].seq.push_back(l); //l为当前点的编号
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
modify(u << 1, l, r);
if (mid < r)
modify(u << 1 | 1, l, r);
pushup(u);
}
void query(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r)
{
pushup(ans, ans, tr[u].seq);
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
query(u << 1, l, r);
if (mid < r)
query(u << 1 | 1, l, r);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
int StartTime = clock();
#endif
scanf("%d%d%d", &n, &m, &q);
fir(i, 1, m)
{
scanf("%d%d%d", &u, &v, &w);
edge[i] = {u, v, w};
}
build(1, 1, m);
for (int i = 1; i <= q; i++)
{
int op;
scanf("%d", &op);
if (op == 2)
{
int l, r;
scanf("%d%d", &l, &r);
ans.clear();
int sum = 0;
query(1, l, r);
if (ans.size() != n - 1)
{
puts("Impossible");
}
else
{
for (auto t : ans)
sum += edge[t].w;
printf("%d\n", sum);
}
}
else
{
int x, y, z, t;
scanf("%d%d%d%d", &x, &y, &z, &t);
edge[x] = {y, z, t};
modify(1, x, x);
}
}
#ifndef ONLINE_JUDGE
printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
return 0;
}
功能强大的线段树