牛客暑期集训第六场题解
zhanglichen
H.动态最小生成树
题意:
小Z喜欢最小生成树。
小Z有一张n*m的图,请你支持两种操作:
(1)修改第x条边为连接点y,z,边权为t。
(2)查询只用编号在[l,r]范围内的边,得到的最小生成树权值是多少。
题解:
猜想一个性质:
对于两个边集A和B,这两个边集合并后的边集C的最小生成树可以直接由A的最小生成树和B的最小生成树合并得到(我不会证)。
观察到点数只有200,考虑用线段树维护,线段树上每个节点维护对应区间内的边集涉及的点集的最小生成树。
线段树pushup的时候,不用排序,定义两个指针x和y分别表示左区间的遍历位置和右区间的遍历位置,采用有序链表合并的方式合并两个区间。
注意:father数组一定要定义在外面,因为程序在同一时间只能运行一层递归函数,只需要每次合并区间的时候初始化一下father[i]即可,如果每个节点都定义一个father数组,会导致超时。
时间复杂度(O(200nlogn))。
// Problem: 动态最小生成树
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/9986/H
// Memory Limit: 1048576 MB
// Time Limit: 10000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//200个点,30000条边
//30000个询问
//对边分块
//每一块的边所包含的点集的最小生成树
//合并点集
//枚举两块边的每条生成树上的边
//取小的边作为新的生成树
//单点修改
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e4+100;
int n,m,q;
struct edge {
int u,v,w;
bool operator < (const edge &r) const {
return w<r.w;
}
}e[maxn];
int father[205];
struct node {
int l,r;
vector<int> p;
long long sum=0;
}segTree[maxn<<2];
int findfather (int x) {
int a=x;
while (x!=father[x]) x=father[x];
while (a!=father[a]) {
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
void build (int i,int l,int r) {
segTree[i].l=l;
segTree[i].r=r;
if (l==r) {
segTree[i].p.push_back(l);
segTree[i].sum=e[l].w;
return;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
for (int j=1;j<=n;j++)father[j]=j;segTree[i].sum=0;segTree[i].p.clear();
int x=0,y=0;
while (x<segTree[i<<1].p.size()||y<segTree[i<<1|1].p.size()) {
if (x<segTree[i<<1].p.size()&&y<segTree[i<<1|1].p.size()) {
if (e[segTree[i<<1].p[x]].w<e[segTree[i<<1|1].p[y]].w) {
int u=findfather(e[segTree[i<<1].p[x]].u);
int v=findfather(e[segTree[i<<1].p[x]].v);
if (u==v) {
x++;
continue;
}
segTree[i].p.push_back(segTree[i<<1].p[x]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1].p[x]].w;
x++;
}
else {
int u=findfather(e[segTree[i<<1|1].p[y]].u);
int v=findfather(e[segTree[i<<1|1].p[y]].v);
if (u==v) {
y++;
continue;
}
segTree[i].p.push_back(segTree[i<<1|1].p[y]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
y++;
}
}
else if (x<segTree[i<<1].p.size()) {
int u=findfather(e[segTree[i<<1].p[x]].u);
int v=findfather(e[segTree[i<<1].p[x]].v);
if (u==v) {
x++;
continue;
}
segTree[i].p.push_back(segTree[i<<1].p[x]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1].p[x]].w;
x++;
}
else {
int u=findfather(e[segTree[i<<1|1].p[y]].u);
int v=findfather(e[segTree[i<<1|1].p[y]].v);
if (u==v) {
y++;
continue;
}
segTree[i].p.push_back(segTree[i<<1|1].p[y]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
y++;
}
}
}
void up (int i,int pp) {
if (segTree[i].l==pp&&segTree[i].r==pp) {
return;
}
int mid=(segTree[i].l+segTree[i].r)>>1;
if (pp<=mid) up(i<<1,pp);
if (pp>mid) up(i<<1|1,pp);
for (int j=1;j<=n;j++)father[j]=j;segTree[i].sum=0;segTree[i].p.clear();
int x=0,y=0;
while (x<segTree[i<<1].p.size()||y<segTree[i<<1|1].p.size()) {
if (x<segTree[i<<1].p.size()&&y<segTree[i<<1|1].p.size()) {
if (e[segTree[i<<1].p[x]].w<e[segTree[i<<1|1].p[y]].w) {
int u=findfather(e[segTree[i<<1].p[x]].u);
int v=findfather(e[segTree[i<<1].p[x]].v);
if (u==v) {
x++;
continue;
}
segTree[i].p.push_back(segTree[i<<1].p[x]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1].p[x]].w;
x++;
}
else {
int u=findfather(e[segTree[i<<1|1].p[y]].u);
int v=findfather(e[segTree[i<<1|1].p[y]].v);
if (u==v) {
y++;
continue;
}
segTree[i].p.push_back(segTree[i<<1|1].p[y]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
y++;
}
}
else if (x<segTree[i<<1].p.size()) {
int u=findfather(e[segTree[i<<1].p[x]].u);
int v=findfather(e[segTree[i<<1].p[x]].v);
if (u==v) {
x++;
continue;
}
segTree[i].p.push_back(segTree[i<<1].p[x]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1].p[x]].w;
x++;
}
else {
int u=findfather(e[segTree[i<<1|1].p[y]].u);
int v=findfather(e[segTree[i<<1|1].p[y]].v);
if (u==v) {
y++;
continue;
}
segTree[i].p.push_back(segTree[i<<1|1].p[y]);
father[u]=v;
segTree[i].sum+=e[segTree[i<<1|1].p[y]].w;
y++;
}
}
}
node query (int i,int l,int r) {
if (segTree[i].l>=l&&segTree[i].r<=r) {
return segTree[i];
}
int mid=(segTree[i].l+segTree[i].r)>>1;
vector<edge> pp;
node ans;
node t1;
node t2;
int x=0,y=0;
if (l<=mid) t1=query(i<<1,l,r);
if (r>mid) t2=query(i<<1|1,l,r);
for (int j=1;j<=n;j++) father[j]=j;
ans.sum=0;
while (x<t1.p.size()||y<t2.p.size()) {
if (x<t1.p.size()&&y<t2.p.size()) {
if (e[t1.p[x]].w<e[t2.p[y]].w) {
int u=findfather(e[t1.p[x]].u);
int v=findfather(e[t1.p[x]].v);
if (u==v) {
x++;
continue;
}
ans.p.push_back(t1.p[x]);
father[u]=v;
ans.sum+=e[t1.p[x]].w;
x++;
}
else {
int u=findfather(e[t2.p[y]].u);
int v=findfather(e[t2.p[y]].v);
if (u==v) {
y++;
continue;
}
ans.p.push_back(t2.p[y]);
father[u]=v;
ans.sum+=e[t2.p[y]].w;
y++;
}
}
else if (x<t1.p.size()) {
int u=findfather(e[t1.p[x]].u);
int v=findfather(e[t1.p[x]].v);
if (u==v) {
x++;
continue;
}
ans.p.push_back(t1.p[x]);
father[u]=v;
ans.sum+=e[t1.p[x]].w;
x++;
}
else {
int u=findfather(e[t2.p[y]].u);
int v=findfather(e[t2.p[y]].v);
if (u==v) {
y++;
continue;
}
ans.p.push_back(t2.p[y]);
father[u]=v;
ans.sum+=e[t2.p[y]].w;
y++;
}
}
return ans;
}
int main () {
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++) {
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
}
build(1,1,m);
while (q--) {
int op;
scanf("%d",&op);
if (op==1) {
int x,y,z,t;
scanf("%d%d%d%d",&x,&y,&z,&t);
e[x].u=y;e[x].v=z;e[x].w=t;
up(1,x);
}
else {
int l,r;
scanf("%d%d",&l,&r);
node ans=query(1,l,r);
int f=1;
for (int i=1;i<=n;i++) if (findfather(i)!=findfather(1)) f=0;
if (!f) {
printf("Impossible
");
continue;
}
printf("%lld
",ans.sum);
}
}
}