bzoj3669[Noi2014]魔法森林

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 150005
#define maxm 100005
#define pi pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std; struct note{
int u,v,a,b;
}wi[maxm];
int n,m,ans,fa[maxn],son[maxn][],val[maxn],sm[maxn],sm_id[maxn];
bool rev[maxn]; bool comp(note x,note y){
if (x.a==y.a) return x.b<y.b;
return x.a<y.a;
} struct date{
int which(int x){
return son[fa[x]][]==x;
}
int isroot(int x){
return son[fa[x]][]!=x&&son[fa[x]][]!=x;
}
void update(int x){
sm_id[x]=x,sm[x]=val[x];
if (son[x][]&&sm[son[x][]]>sm[x]) sm[x]=sm[son[x][]],sm_id[x]=sm_id[son[x][]];
if (son[x][]&&sm[son[x][]]>sm[x]) sm[x]=sm[son[x][]],sm_id[x]=sm_id[son[x][]];
}
void pushdown(int x){
if (rev[x]){
rev[x]^=,swap(son[x][],son[x][]);
if (son[x][]) rev[son[x][]]^=;
if (son[x][]) rev[son[x][]]^=;
}
}
void relax(int x){
if (!isroot(x)) relax(fa[x]);
pushdown(x);
}
void rotata(int x){
int y=fa[x],d=which(x),dd=which(y);
if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y];
fa[son[x][d^]]=y,son[y][d]=son[x][d^];
fa[y]=x,son[x][d^]=y;
update(y);
}
void splay(int x){
relax(x);
while (!isroot(x)){
if (isroot(fa[x])) rotata(x);
else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x);
else rotata(x),rotata(x);
}
update(x);
}
void access(int x){
for (int p=;x;x=fa[x]){
splay(x);
son[x][]=p;
update(x);
p=x;
}
}
void make_root(int x){
access(x);
splay(x);
rev[x]^=;
}
void link(int x,int y){
make_root(x);
fa[x]=y;
}
void cut(int x,int y){
make_root(x);
access(y);
splay(y);
son[y][]=fa[x]=;
update(y);
}
void split(int x,int y){
make_root(x);
access(y);
splay(y);
}
int query(int x,int y){
split(x,y);
return sm[y];
}
pi find(int x,int y){
split(x,y);
return mp(sm_id[y],sm[y]);
}
int find_root(int x){
access(x);
splay(x);
while (son[x][]) x=son[x][];
return x;
}
}lct; int main(){
// freopen("forest.in","r",stdin);
// freopen("forest.out","w",stdout);
memset(rev,,sizeof(rev));
memset(fa,,sizeof(fa));
memset(son,,sizeof(son));
memset(val,,sizeof(val));
memset(sm,,sizeof(sm));
scanf("%d%d",&n,&m);
for (int i=;i<=m;i++) scanf("%d%d%d%d",&wi[i].u,&wi[i].v,&wi[i].a,&wi[i].b);
sort(wi+,wi+m+,comp);
for (int i=;i<=n;i++) val[i]=,lct.update(i);
ans=maxn;
pi temp;
for (int i=;i<=m;i++){
int u=wi[i].u,v=wi[i].v;
if (lct.find_root(u)!=lct.find_root(v)){
val[n+i]=wi[i].b,lct.update(n+i);
lct.link(n+i,u),lct.link(n+i,v);
}else{
temp=lct.find(u,v);
if (temp.second<=wi[i].b) continue;
else{
int t=temp.first;
lct.cut(wi[t-n].u,t),lct.cut(wi[t-n].v,t);
val[n+i]=wi[i].b,lct.update(n+i);
lct.link(n+i,u),lct.link(n+i,v);
}
}
if (lct.find_root()!=lct.find_root(n)) continue;
int t=lct.query(,n);
ans=min(ans,t+wi[i].a);
}
if (ans>maxm) printf("-1\n");
else printf("%d\n",ans);
return ;
}

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3669

题目大意:给定一个无向图,每条边有两个权值va,vb,要求选一条从1到n的路径,满足这条路径上点的max(va)+max(vb)最小,若没有从A到B的路径,则输出-1。

做法:初看这题,暴力写法:将边按va升序排序,枚举i,此时max(va)=vi,保证max(vb)最小即可,我们可以想到kruscal,并查集集维护即可,但是瓶颈在于每次都要将1~i的边按vb升序排序,在O(n)的加入,这种做法复杂度过高,不宜使用。

仔细想想:我们可以考虑用lct维护这个过程,考虑先将边按va升序排序,然后依次加入每一条边,此时max(va)=vi,保证max(vb)最小即可,加入该边时会有两种情况:

1.不形成环,则加入这条边,若节点1与节点n联通,则用1到n链上vb最大值+vi更新答案,否则不更新答案。

2.形成环,与lct模拟kruscal的过程一样,删掉原本那条链上vb权值最大的边,并加入这条边,若节点1与节点n联通,则用1到n链上vb最大值+vi更新答案,否则不更新答案。

lct+离线处理

上一篇:Raspberry Pi 4B 使用OpenCV访问摄像头picamera模块


下一篇:URAL1900 Brainwashing Device(dp)