魔法森林
题解
看到这道题应该是很容易想到贪心,我们可以先将所有边根据值排序,再当前基础上找到所需值最小的一条路径。
但是由于边会产生改变,于是每次都要跑一遍最短路,明显是会T飞的。
很容易发现,当前加入的一条边只对部分的路径产生影响,而这些路径都是在已有路径的最小生成树上构成一个环的。由于当前到每个点的最优路径都会对以后的答案产生影响,所以我们要实时维护当前最小生成树的形态。
每次加入边时如果这个环上最劣的边比加入的这条边值更大,就将其删去,再加入当前边。于是我们就需要用LCT来维护这棵最小生成树。
但是于此同时我们如何维护兩(两)点之间连得边呢?我们可以将这些边建成虚点,每次连接就将两个端点连到虚点上,这样就不需要每次更改后都很麻烦的维护点权了,因为每次修改后点的父子关系都是会变的。
这样每次加入边后都维护出当前最优值,答案就是它们中最小的。
时间复杂度,可以过的。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
typedef long long LL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int val[MAXN],fa[MAXN],n,m,ans;
struct edge{int u,v,a,b;}e[MAXN];
bool cmp(edge x,edge y){return x.b<y.b;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){
int u=findSet(a),v=findSet(b);
if(u==v)return ;fa[u]=v;
}
class Link_Cut_Tree{
private:
int rev[MAXN],lzy[MAXN],maxp[MAXN];
int father[MAXN],ch[MAXN][2],sta[MAXN],stak,siz[MAXN];
void updata(int p){
siz[p]=1+siz[ch[p][0]]+siz[ch[p][1]];maxp[p]=p;
if(ch[p][0]&&val[maxp[ch[p][0]]]>val[maxp[p]])
maxp[p]=maxp[ch[p][0]];
if(ch[p][1]&&val[maxp[ch[p][1]]]>val[maxp[p]])
maxp[p]=maxp[ch[p][1]];
}
void downdata(int p){
if(rev[p]){
swap(ch[p][0],ch[p][1]);rev[p]^=1;
rev[ch[p][0]]^=1;rev[ch[p][1]]^=1;
}
}
bool identify(int x){return ch[father[x]][1]==x;}
bool isRoot(int x){return ch[father[x]][0]^x&&ch[father[x]][1]^x;}
void connect(int x,int fa,int d){father[x]=fa;ch[fa][d]=x;}
void rotate(int x){
int y=father[x],z=father[y];
int d1=identify(y),d2=identify(x);
int B=ch[x][d2^1];father[x]=z;
if(!isRoot(y))connect(x,z,d1);
connect(B,y,d2);connect(y,x,d2^1);
updata(y);updata(x);
}
void splay(int x){
stak=0;sta[++stak]=x;int y=x;
while(!isRoot(y))sta[++stak]=y=father[y];
while(stak)downdata(sta[stak--]);
for(y=father[x];!isRoot(x);rotate(x),y=father[x])
if(!isRoot(y))rotate(identify(x)==identify(y)?y:x);
updata(x);
}
void access(int x){
for(int y=0;x;x=father[y=x])
splay(x),ch[x][1]=y,updata(x);
}
void makeRoot(int x){access(x);splay(x);rev[x]^=1;}
int findRoot(int x){
access(x);splay(x);downdata(x);
while(ch[x][0])downdata(x=ch[x][0]);
return x;
}
public:
int split(int x,int y){makeRoot(x);access(y);splay(y);return maxp[y];}
void linkTree(int u,int v){makeRoot(u);if(findRoot(v)!=u)father[u]=v;}
void cutTree(int u,int v){
makeRoot(u);if(findRoot(v)^u)return ;
access(v);splay(v);ch[v][0]=father[u]=0;updata(v);
}
}Tree;
signed main(){
read(n);read(m);ans=INF;
for(int i=1;i<=m;i++)read(e[i].u),read(e[i].v),read(e[i].a),read(e[i].b);
sort(e+1,e+m+1,cmp);makeSet(n+m);for(int i=n+1;i<=n+m;i++)val[i]=e[i-n].a;
for(int i=1;i<=m;i++){
int u=e[i].u,v=e[i].v;bool fg=1;
if(findSet(u)==findSet(v)){
int w=Tree.split(u,v);
if(val[w]>e[i].a)
Tree.cutTree(e[w-n].u,w),Tree.cutTree(w,e[w-n].v);
else fg=0;
}
else unionSet(u,v);if(fg)Tree.linkTree(u,i+n),Tree.linkTree(i+n,v);
if(findSet(1)==findSet(n))ans=min(ans,e[i].b+val[Tree.split(1,n)]);
}
if(ans<INF-1)printf("%d\n",ans);else puts("-1");
return 0;
}