P2387 [NOI2014] 魔法森林
P2387 [NOI2014] 魔法森林
题目描述
为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在
只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边
由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的个数与 B 型守护精灵的个数之和。
输入格式
输入文件的第
输出格式
输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出 -1
。
Solution:
好神的 LCT。本来我想了个逆天的 kurskal 重构树,但是我们发现可以轻松被 hack.
我们先介绍一下如何使用 LCT 维护最小生成树:我们将 LCT 上的点分为两类,一类是原图上的点,第二类是原图上的边,我暂且称之为虚点,感觉和 kruskal 重构树其实挺像的 (所以我开局算是想对了一半?),我们在添加一条边时新建一个点,其点权为其边权
然后就是本题题解:我们先对所有边按
Code:
#include<bits/stdc++.h> const int N=1e5+5; const int inf=1e9; using namespace std; int st[N]; int w[N<<1]; struct LCT{ struct Tree{ int tag,ff,ch[2],mx,pos; }t[N<<2]; #define ls t[x].ch[0] #define rs t[x].ch[1] #define fa t[x].ff inline bool isroot(int x) { return (t[fa].ch[0]==x||t[fa].ch[1]==x); } inline void pushup(int x) { t[x].mx=w[x];t[x].pos=x; if(ls&&t[ls].mx > t[x].mx) t[x].mx=t[ls].mx,t[x].pos=t[ls].pos; if(rs&&t[rs].mx > t[x].mx) t[x].mx=t[rs].mx,t[x].pos=t[rs].pos; return; } inline void rev(int x) { swap(t[x].ch[0],t[x].ch[1]); t[x].tag^=1; return ; } inline void pushdown(int x) { if(t[x].tag) { if(ls)rev(ls); if(rs)rev(rs); t[x].tag=0; } return ; } inline void rotate(int x) { int y=fa,z=t[fa].ff,k=t[fa].ch[1]==x ? 1 : 0; if(isroot(y))t[z].ch[t[z].ch[1]==y]=x; t[x].ff=z; t[y].ch[k]=t[x].ch[!k]; if(t[x].ch[!k])t[t[x].ch[!k]].ff=y; t[x].ch[!k]=y; t[y].ff=x; pushup(y); } inline void splay(int x) { int y=x,z=0; st[++st[0]]=y; while(isroot(y))st[++st[0]]=y=t[y].ff; while(st[0])pushdown(st[st[0]--]); while(isroot(x)) { y=fa,z=t[fa].ff; if(isroot(y)){rotate((t[y].ch[1]==x)==(t[z].ch[1]==y) ? y : x);} rotate(x); } pushup(x); } void access(int x) { int y=0; while(x) { splay(x);rs=y;pushup(x); y=x;x=fa; } } void make_root(int x) { access(x);splay(x); rev(x); } int find(int x) { access(x);splay(x); while(ls)pushdown(x),x=ls; splay(x); return x; } void splite(int x,int y) { make_root(x); access(y);splay(y); } void link(int x,int y) { make_root(x); if(find(y)!=x)t[x].ff=y; return ; } void cut(int x) // 这里是减去一个点 x { splay(x); t[ls].ff=t[rs].ff=0; return ; } bool check(int x,int y) { make_root(x); return find(y)!=x; } }T; struct Edge{ int u,v,a,b; bool operator <(const Edge ee)const { return a<ee.a; } }e[N]; int n,m,tot,ans=inf; void work() { cin>>n>>m;tot=n; for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b); } sort(e+1,e+1+m); for(Edge E : e) { int u=E.u,v=E.v,a=E.a,b=E.b; w[++tot]=b; if(u==v)continue; if(T.check(u,v)){T.link(u,tot),T.link(tot,v);} else { T.splite(u,v);int mx= T.t[v].mx,pos=T.t[v].pos; if(b>=mx)continue; else { T.cut(pos); T.link(u,tot),T.link(tot,v); } } if(!T.check(1,n)) { T.splite(1,n); ans = min(ans,T.t[n].mx+a); } } printf("%d\n",(ans==inf ? -1 : ans)); } int main() { //freopen("forest.in","r",stdin);freopen("forest.out","w",stdout); work(); return 0; }