【BZOJ】【P3669】【NOI2014】【魔法森林】【题解】【LCT】

传送门:www.lydsy.com:808/JudgeOnline/problem.php?id=3669

考场上弱……写了40分……考完加了两行优化……就TM70了!卧槽……

40/70分算法:

枚举a,排序b,从小到大跑生成树,注意不能二分,a不满足二分性质

优化:只枚举a出现过的值,就可以70……唉……弱……

100分算法:

把边按a从小到大排序
然后一条条加边
动态维护b的最小生成树
假设现在加入的边是(x,y) a b
那么答案=min(答案,a+树上1到n路径上的b的最大值)
用LCT维护

40分Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int maxm=1e5+10;
int n,m,mid,ans=INT_MAX;
int getint(){
	int x;scanf("%d",&x);return x;
}
void putint(int x){
	printf("%d\n",x);
}
struct edge{
	int u,v,a,b;
	edge(int _u=0,int _v=0,int _a=0,int _b=0):
		u(_u),v(_v),a(_a),b(_b){}
	bool operator<(const edge &x)const{
		return b<x.b;
	}
};
int fa[maxn];
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int find(int x){if(fa[x]!=x)return fa[x]=find(fa[x]);return x;}
void Union(int a,int b){fa[find(a)]=find(b);}
edge edges[maxm];
bool ok(){
	init();
	int maxB=0;
	for(int i=1;i<=m;i++){
		int u=edges[i].u,v=edges[i].v,a=edges[i].a,b=edges[i].b;
		if(a>mid)continue;
		int f1=find(u),f2=find(v);
		if(f1==f2)continue;
		Union(f1,f2);
		maxB=max(maxB,b);
		if(find(1)==find(n))break;
	}
	if(find(1)!=find(n))return false;
	else{
		ans=min(ans,mid+maxB);
		return true;
	}
}
int main(){
	int maxa=0;
	n=getint();m=getint();
	init();
	for(int i=1;i<=m;i++){
		edges[i].u=getint();
		edges[i].v=getint();
		edges[i].a=getint();
		edges[i].b=getint();
		if(edges[i].u==edges[i].v){
			edges[i].a=INT_MAX;
			continue;
		}
		maxa=max(maxa,edges[i].a);
		Union(edges[i].u,edges[i].v);
	}if(find(1)!=find(n)){
		puts("-1");return 0;
	}sort(edges+1,edges+1+m);
	for(mid=0;mid<=maxa;mid++)
	ok();
	cout<<ans<<endl;
	return 0;
}

抄了wangxz神犇的LCT模板好久终于A了……

AC Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int maxm=1e5+10;
int n,m,mid,ans=INT_MAX;
int getint(){
	char ch=0;int res=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))(res*=10)+=ch-'0',ch=getchar();
	return res;
}
void putint(int x){
	printf("%d\n",x);
}
struct edge{
	int u,v,a,b;
	edge(int _u=0,int _v=0,int _a=0,int _b=0):
		u(_u),v(_v),a(_a),b(_b){}
	bool operator<(const edge &x)const{
		return a<x.a;
	}
};
int fa[maxn];
int find(int x){if(fa[x]!=x)return fa[x]=find(fa[x]);return x;}
void Union(int a,int b){fa[find(a)]=find(b);}
edge edges[maxm];
struct node{
	int rev,val;
	node *c[2],*p,*maxx;
	node(){
		rev=val=0;
		maxx=this;
	}
	void pushdown(){
		if(rev){
			c[0]->rev^=1;
			c[1]->rev^=1;
			swap(c[0],c[1]);
			rev^=1;
		}
	}
	node* rz(){
		maxx=this;
		if(c[0]->maxx->val>maxx->val)maxx=c[0]->maxx;
		if(c[1]->maxx->val>maxx->val)maxx=c[1]->maxx;
		return this;
	}
	void sets(node *x,int d){
		pushdown();
		(c[d]=x)->p=this;
		rz();
	}
	bool d(){
		return p->c[1]==this;
	}
	bool root(){
		return p->c[0]!=this&&p->c[1]!=this;
	}
}pool[maxn+maxm];
void newnode(node *x,int i){
	static int tot=0;
	x=pool+(++tot);
	x->rev=0;x->val=edges[i].b;x->maxx=x;
	x->c[0]=x->c[1]=x->p=pool;
}
void rot(node *x){
    node *y=x->p;
    if(!y->root())y->p->pushdown();
    y->pushdown();
    x->pushdown();
    int d=x->d();
    y->sets(x->c[!d],d);
    if(y->root())x->p=y->p;
    else y->p->sets(x,y->d());
    x->sets(y,!d);
}
void splay(node *x){
    for(;!x->root();rot(x))
    if(x->p->root());
    else if(x->d()==x->p->d())rot(x->p);
    else rot(x);
}
node *access(node *x){
	node *y=pool;
	for(;x!=pool;x=x->p){
		splay(x);
		x->sets(y,1);
		y=x;
	}return y;
}
void makert(node *x){
	access(x)->rev^=1;
	splay(x);
}
node *qmax(node *u,node *v){
	makert(u);
	access(v);
	splay(v);
	return v->maxx;
}
node *findrt(node *x){
	for(x=access(x);x->pushdown(),x->c[0]!=pool;x=x->c[0]);
	return x;
}
void link(node *x,node *y){
    makert(x);
    x->p=y;
    access(x);
}
void cut(node *y){
	splay(y);
	y->c[0]->p=y->p;
	y->c[1]->p=pool;
	y->c[0]=y->c[1]=y->p=pool;
	y->maxx=y;
	y->val=0;
}
void add(node *u,node *v,node *w){
	if(findrt(u)==findrt(v)){
		node *x=qmax(u,v);
		if(x->val<w->val)return;
		cut(x);
		v->rz();
		link(v,w);
		link(w,u);
	}else{
		link(v,w);
		link(w,u);
	}
}
#define id(i) (pool+i)
int main(){
	n=getint();m=getint();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		edges[i].u=getint();
		edges[i].v=getint();
		edges[i].a=getint();
		edges[i].b=getint();
		Union(edges[i].u,edges[i].v);
	}if(find(1)!=find(n)){
		puts("-1");return 0;
	}sort(edges+1,edges+1+m);
	for(int i=0;i<=n+m;i++)
		id(i)->p=id(i)->c[0]=id(i)->c[1]=pool,id(i)->maxx=id(i);
	int ans=INT_MAX;
	for(int i=1;i<=m;i++){
		id(i+n)->val=edges[i].b;
		node *u=edges[i].u+pool,*v=edges[i].v+pool,*w=i+n+pool;
		add(u,v,w);
		if(findrt(id(1))==findrt(id(n)))
		ans=min(ans,edges[i].a+qmax(id(1),id(n))->val);
	}cout<<ans<<endl;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值