您的位置:首页 > 其它

BZOJ 3669: [Noi2014]魔法森林

2017-05-18 19:32 387 查看

题目

BZOJ 3669: [Noi2014]魔法森林

题解

感觉收获就是get了一种除了Kruskal和prim之外的最小生成树算法。

理论复杂度貌似是mlogn,不过常数巨大堪比一个log(此句话引用自某论文)。

陈年老题谁都会做,我没做出来qwq。边权按a排序之后做b的最小生成树。对于一条连接(u,v)的边,如果uv不连通那么连起来,否则找u->v最大的边权,然后切掉,把当前边加进去,当1,n联通的时候可以更新答案(我tm还zz的写成了弄出来一棵生成树的时候才有答案,好像也有很多人和我一样(雾))。边权不好维护,把边抽象成点,用点权维护。

然后就是splay维护最大值和最大值出现位置了。easy。感觉lct的板子总是哪里要打错然后调很久233,。不过调的越来越熟练了。自己发明了一套成体系的调试方法,对于指针党简直是太棒了。

代码

//QWsin
#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=200000+10;
const int maxm=100000+10;

map<int,int>id;

int n,m;
struct Edge
{
int u,v,a,b;
inline void input(){scanf("%d%d%d%d",&u,&v,&a,&b);}
inline bool operator < (const Edge &rhs)const{
return a<rhs.a||(a==rhs.a&&b<rhs.b);
}
} e[maxm];

struct Node
{
int v,pos,mx,posx,rev;
Node *ch[2],*fa;
Node(){v=pos=mx=posx=rev=0;ch[0]=ch[1]=fa=NULL;}
Node(int v,int pos):v(v),pos(pos){mx=v,posx=pos;rev=0;ch[0]=ch[1]=fa=NULL;}
inline void push(){
if(rev){
swap(ch[0],ch[1]);
if(ch[0]) ch[0]->rev^=1;
if(ch[1]) ch[1]->rev^=1;
rev=0;
}
}
inline void up()
{
mx=v,posx=pos;
if(ch[0] && ch[0]->mx > mx) mx=ch[0]->mx,posx=ch[0]->posx;
if(ch[1] && ch[1]->mx > mx) mx=ch[1]->mx,posx=ch[1]->posx;
};

inline void out(){
if(!this) {puts("NULL");return ;}
printf("%d:\n",id[(int)this]);
printf("    ch[0]=%d\n",id[(int)ch[0]]);
printf("    ch[1]=%d\n",id[(int)ch[1]]);
printf("    fa=%d\n",id[(int)fa]);
printf("    val=%d\n",v);
printf("    mx=%d\n",mx);
printf("    posx=%d\n",posx);
puts("\n");
}
}*tmp[maxn];

struct LinkCutTree
{

Node *node[maxn];
inline void init()
{
id[0]=0;
for(int i=1; i<=n; ++i) node[i]=new Node(),id[(int)node[i]]=i;
for(int i=1; i<=m; ++i) node[n+i]=new Node(e[i].b,i),id[(int)node[n+i]]=n+i;
}

inline int pd(Node* p){return p->fa->ch[1]==p;}
inline int is_root(Node* p){return (!(p->fa))||(p->fa->ch[0]!=p&&p->fa->ch[1]!=p);}
inline void rotate(Node* p)
{
int c=pd(p)^1;
Node *t=p->fa;
t->ch[c^1]=p->ch[c];
if(p->ch[c]) p->ch[c]->fa=t;
p->fa=t->fa;
if(!is_root(t)) p->fa->ch[pd(t)]=p;
t->fa=p;p->ch[c]=t;t->up();p->up();
//      p->out();
}

inline void splay(Node *p)
{
int top=0;
for(Node* t=p;; t=t->fa)
{
tmp[++top]=t;
if(is_root(t)) break;
}
for(; top>=1; --top) tmp[top]->push();
for(; !is_root(p); rotate(p))
if(!is_root(p->fa)) rotate(pd(p)==pd(p->fa) ? p->fa:p);
}

inline void access(Node* p)
{
//      p->out();
for(Node* pre=NULL; p ; pre=p,p=p->fa){
splay(p),p->ch[1]=pre,p->up();
//          pre->out();p->out();
}
}

inline void make_root(Node* p){access(p);splay(p);p->rev=1;}
inline void link(Node* a,Node *b){
//      a->out();b->out();
make_root(a);a->fa=b;
//      a->out();b->out();
}

inline void cut(Node* a,Node *b){
make_root(a);access(b);splay(b);
a->fa=b->ch[1]=NULL;b->up();
}

inline void link(int i){
link(node[e[i].u],node[n+i]);
link(node[n+i],node[e[i].v]);
}

inline void cut(int i){
cut(node[e[i].u],node[i+n]);
cut(node[i+n],node[e[i].v]);
}

inline int getmx(int u,int v,int &id) //u->v路径最大值
{
make_root(node[v]);
access(node[u]);
splay(node[u]);
id=node[u]->posx;
return node[u]->mx;
}

//在u->v路径上找到最大的决定要不要换
inline void find_mx(int i)
{
int id,val=getmx(e[i].u,e[i].v,id);
if(val > e[i].b){cut(id);link(i);}
}
} lct;

inline void init_data()
{
cin>>n>>m;
for(int i=1; i<=m; ++i) e[i].input();
sort(e+1,e+m+1);
}

int p[maxn];
int findset(int x){return p[x]==x?x:p[x]=findset(p[x]);}

inline void solve()
{
for(int i=1; i<=n; ++i) p[i]=i;
lct.init();

int ans=1<<30,cnt=n,bin;
for(int i=1; i<=m; ++i)
{
if(e[i].a > ans) break;
int u=e[i].u,v=e[i].v;
u=findset(u);
v=findset(v);
if(u!=v){lct.link(i); p[u]=v; --cnt;}
else  lct.find_mx(i);
if(findset(1)==findset(n))
ans=min(ans,e[i].a+lct.getmx(1,n,bin));
}
if(findset(1)!=findset(n)) puts("-1");
else cout<<ans<<endl;
}

int main()
{
init_data();
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: