[bzoj3664][Noi2014]魔法森林 Link-Cut-Tree 并查集

3669: [Noi2014]膜法森林

Time Limit: 30 Sec   Memory Limit: 512 MB
[ Submit][ Status][ Discuss]

Description

为了得到人生的经验,E同学下定决心去拜访住在膜法森林中的长者。膜法森林可以被一个包含个N节点M条边的无向图节点标号为1..N边标号为1..M。初始时E同学在号节点1,长者则住在号节点N。小E需要通过这一片膜法森林,才能够拜访到长者。

膜法森林中有一些想搞大新闻的香港记者暗中观察。每当有人经过一条边时候,这条边上的香港记者就会对其发起攻击。幸运的是,在号节点住着两种蛤A型蛤与B型蛤E可以借助它们的力量,达到自己的目的

只要E带上足够多的蛤,香港记者们就不会发起攻击了。具体来说无向图中的每一条边Ei包含两个权值Ai与Bi身上携带的A型蛤个数不少于AiB型蛤个数不少于Bi这条边上的香港记者不会对通过这条边人发起攻击当且仅当通过片膜法森林的过程中没有任意一条边香港记者E发起攻击他才能成功找到长者

由于携带蛤是一件非常麻烦的事,小E想要知道,要能够成功拜访到长者,最少需要携带蛤的总个数总个数A型蛤的个数与B型蛤的个数之和。

Input

第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

Output

输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

Sample Input

【输入样例1】
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17





【输入样例2】


3 1
1 2 1 1



Sample Output

【输出样例1】

32
【样例说明1】
如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
综上所述,小E最少需要携带32个守护精灵。



【输出样例2】


-1
【样例说明2】
小E无法从1号节点到达3号节点,故输出-1。

HINT

2<=n<=50,000


0<=m<=100,000




1<=ai ,bi<=50,000

Source

noi2014有80个人A了这题
正解是LCT,然而SPFA要快得多
类似kruskal的做法
先按A的大小连边
然后每次加点,如果存在一个环,就要减去前面的B值最大的(想一想为什么只管B的值?)
然后如果找到1到n的路径,就更新答案
#include
#include
#include
#define inf 1000000000
using namespace std;
const int N = 200000 + 5;
int mx[N],va[N],p[N],fa[N],c[N][2],sta[N],ans=inf,top,n,m;
bool rev[N];
struct data{
	int u,v,a,b;
}e[N];
int find( int x ){ return p[x] == x ? x : p[x] = find(p[x]); }
bool cmp( data a, data b ){ return a.a < b.a; }
bool isroot( int x ){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; }
void update( int x ){
	mx[x] = x;
	if( va[mx[c[x][1]]] > va[mx[x]] ) mx[x] = mx[c[x][1]];
	if( va[mx[c[x][0]]] > va[mx[x]] ) mx[x] = mx[c[x][0]];
}
void pushdown( int x ){
	if( rev[x] ){
		swap(c[x][0],c[x][1]);
		rev[c[x][0]] ^= 1; rev[c[x][1]] ^= 1; rev[x] = 0;
	}
}
void rotate( int x ){
	int y = fa[x], z = fa[y], l, r;
	l = (c[y][1]==x); r = l^1;
	if( !isroot(y) ) c[z][y==c[z][1]] = x;
	fa[x] = z; fa[y] = x; fa[c[x][r]] = y;
	c[y][l] = c[x][r]; c[x][r] = y;
	update(y); update(x);
}
void splay( int x ){
	sta[++top] = x;
	for( int i = x; !isroot(i); i = fa[i] ) sta[++top] = fa[i];
	while( top ) pushdown(sta[top--]);
	while( !isroot(x) ){
		int y = fa[x], z = fa[y];
		if( !isroot(y) ){
			if( (c[y][0]==x)^(c[z][0]==y) ) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
}
void access( int x ){
	for( int t = 0; x; t = x, x = fa[x] ){
		splay(x); c[x][1] = t; update(x);
	}
}
void move_to_root( int x ){
	access(x); splay(x); rev[x] ^= 1;
}
void link( int x, int y ){
	move_to_root(x); fa[x] = y;
}
void cut( int x, int y ){
	move_to_root(x); access(y); splay(y); fa[x] = c[y][0] = 0;
}
int query( int x, int y ){
	move_to_root(x); access(y); splay(y); return mx[y];
}
int main(){
	scanf("%d%d", &n, &m);
	for( int i = 1; i <= m; i++ ) scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);
	for( int i = 1; i <= n; i++ ) p[i] = i;
	sort(e+1,e+m+1,cmp);
	for( int i = 1,u,v,a,b,c; i <= m; i++ ){
		u = e[i].u; v = e[i].v; a = e[i].a; b = e[i].b;
		if( find(u) == find(v) ){
			c = query(u,v);
			if( va[c] > e[i].b ){
				cut( c, e[c-n].u );
				cut( c, e[c-n].v );
			} else continue;
		} else p[find(u)] = find(v);
		va[n+i] = e[i].b; mx[n+i] = n+i;
		link(u,n+i); link(v,n+i);
		if( find(1) == find(n) ) ans = min( ans, a + va[query(1,n)]);
	}
	if( ans == inf ){ printf("-1"); return 0; }
	printf("%d", ans);
	return 0;
}
这是lemonoil的SPFA,快多了,还在第二页,具体没有研究过
放在这里存一下
 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define rez(i,x,y) for(int i=x;i>=y;i--)
#define res(i,x,y) for(int i=x;i<=y;i++)
#define INF 2100000000
#define ll long long
#define M 50500
#define clr(x)  memset(x,0,sizeof(x))
 
using namespace std;
struct node{  
    int to,f,next;  
}table[M<<2];
int head[M],tot;  
struct edges{  
    int x,y,a,b;
    bool operator < (const edges &rhs)const{  
        return a< rhs.a ;  
    } 
}e[M<<1];
int n,m,ans=0x3f3f3f3f,f[M],heap[M],pos[M],top;  
void push_up(int x){  
    int t=pos[x];  
    while(t>1&&f[heap[t]]>1]])  
        swap(heap[t],heap[t>>1]),swap(pos[heap[t]],pos[heap[t>>1]]),t>>=1;  
}  
void insert(int x){  
    heap[++top]=x;  
    pos[x]=top;  
    push_up(x);  
}  
void pop(){  
    pos[heap[1]]=0;  
    heap[1]=heap[top];  
    heap[top--]=0;  
    pos[heap[1]]=1;  
    int t=2;  
    while(t<=top){  
        if(tf[heap[t+1]])t++;  
        if(f[heap[t]]>1]])  
            swap(heap[t],heap[t>>1]),swap(pos[heap[t]],pos[heap[t>>1]]),t<<=1;  
        else break;  
    }  
}  
void SPFA(){  
    while(top){  
        int x=heap[1];pop();  
        for(int i=head[x];i;i=table[i].next)  
            if(f[table[i].to]>max(f[x],table[i].f)){  
                f[table[i].to]=max(f[x],table[i].f);  
                if(!pos[table[i].to])  
                    insert(table[i].to);  
                else 
                    push_up(table[i].to);  
            }  
    }  
}  
void add(int x,int y,int z){  
    table[++tot].to=y;  
    table[tot].f=z;  
    table[tot].next=head[x];  
    head[x]=tot;  
}
inline void readin(int &x){  
    static char ch;
    do ch=getchar();while(ch<'0'||ch>'9');
    while(ch>='0'&&ch<='9')x*=10,x+=ch-'0',ch=getchar();  
}  
int main(){
    readin(n),readin(m);
    for(int i=1;i<=m;i++)
        readin(e[i].x),readin(e[i].y),readin(e[i].a),readin(e[i].b);  
    sort(e+1,e+m+1);
    memset(f,0x3f,sizeof(f));  
    f[1]=0;  
    for(int i=1;i<=m;i++){  
        add(e[i].x,e[i].y,e[i].b);  
        add(e[i].y,e[i].x,e[i].b);  
        if(f[e[i].x]>f[e[i].y])  
            swap(e[i].x,e[i].y);  
        if(max(f[e[i].x],e[i].b)



你可能感兴趣的:(kruskal,并查集,Link-Cut-Tree,bzoj,NOI)