zoukankan      html  css  js  c++  java
  • [BZOJ 3669] [Noi2014] 魔法森林 【LCT】

    题目链接:BZOJ - 3669

    题目分析

    如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小。

    其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径。只能使用 Ai <= x 的边。

    那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了。

    如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大的边的边权大于 w ,那么就删掉那条边权最大的边,然后把这条新加入的边连到 MST 中。

    备注:在 Cut 不够优的边时,我之前将代码写出了非常可怕的 BUG ,但是由于神奇的写法的巧合,这个 BUG 并没有造成后果,我用错误的代码 AC 了两道题..

    现在下方的代码是正确的了。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    inline void Read(int &Num)
    {
    	char c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	Num = c - '0'; c = getchar();
    	while (c >= '0' && c <= '9')
    	{
    		Num = Num * 10 + c - '0';
    		c = getchar();
    	}
    }
    
    inline int gmax(int a, int b) {return a > b ? a : b;}
    inline int gmin(int a, int b) {return a < b ? a : b;}
    
    const int MaxN = 50000 + 5, MaxM = 100000 + 5, MaxT = 150000 + 5, INF = 999999999;
    
    int n, m, Ans;
    int Father[MaxT], Son[MaxT][2], V[MaxT], T[MaxT];
    
    bool isRoot[MaxT], Rev[MaxT];
    
    struct ES
    {
    	int u, v, p, q;
    } E[MaxM];
    
    inline bool Cmp(ES e1, ES e2)
    {
    	return e1.p < e2.p;
    }
    
    /**************************** LCT Start *******************************/
    
    inline int Tmax(int a, int b) {return V[a] > V[b] ? a : b;}
    
    inline void Update(int x)
    {
    	T[x] = Tmax(x, Tmax(T[Son[x][0]], T[Son[x][1]]));
    }
    
    inline void Reverse(int x)
    {
    	Rev[x] = !Rev[x];
    	swap(Son[x][0], Son[x][1]);
    }
    
    inline void PushDown(int x)
    {
    	if (!Rev[x]) return;
    	Rev[x] = false;
    	if (Son[x][0]) Reverse(Son[x][0]);
    	if (Son[x][1]) Reverse(Son[x][1]);
    }
    
    inline int GetDir(int x)
    {
    	if (x == Son[Father[x]][0]) return 0;
    	else return 1;
    }
    
    void Rotate(int x)
    {
    	int y = Father[x], f;
    	PushDown(y); PushDown(x);
    	f = GetDir(x) ^ 1;	
    	if (isRoot[y])
    	{
    		isRoot[y] = false;
    		isRoot[x] = true;
    	}
    	else
    	{
    		if (y == Son[Father[y]][0]) Son[Father[y]][0] = x;
    		else Son[Father[y]][1] = x;
    	}
    	Father[x] = Father[y];
    	Son[y][f ^ 1] = Son[x][f];
    	if (Son[x][f]) Father[Son[x][f]] = y;
    	Son[x][f] = y;
    	Father[y] = x;
    	Update(y); Update(x);
    }
    
    void Splay(int x)
    {
    	int y;
    	while (!isRoot[x])
    	{
    		y = Father[x];
    		if (isRoot[y])
    		{
    			Rotate(x);
    			break;
    		}
    		if (GetDir(y) == GetDir(x)) Rotate(y);
    		else Rotate(x);
    		Rotate(x);
    	}
    }
    
    int Access(int x)
    {
    	int y = 0;
    	while (x != 0)
    	{
    		Splay(x);
    		PushDown(x);
    		if (Son[x][1]) isRoot[Son[x][1]] = true;
    		Son[x][1] = y;
    		if (y) isRoot[y] = false;
    		Update(x);
    		y = x;
    		x = Father[x];
    	}
    	return y;
    }
    
    inline void Make_Root(int x)
    {
    	int t = Access(x);
    	Reverse(t);
    }
    
    inline void Link(int x, int y)
    {
    	Make_Root(x);
    	Splay(x);
    	Father[x] = y;
    }
    
    inline void Cut(int x, int y)
    {
    	Make_Root(x);
    	Access(y);
    	Splay(y);
    	PushDown(y);
    	isRoot[Son[y][0]] = true;
    	Father[Son[y][0]] = 0;
    	Son[y][0] = 0;
    	Update(y);
    }
    
    inline int Find_Root(int x)
    {
    	int t = Access(x);
    	while (Son[t][0] != 0) t = Son[t][0];
    	return t;
    }
    
    /**************************** LCT End *******************************/
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; ++i)
    	{
    		Read(E[i].u); Read(E[i].v); 
    		Read(E[i].p); Read(E[i].q);
    	}
    	sort(E + 1, E + m + 1, Cmp); // by ES.p
    	for (int i = 1; i <= m; ++i) V[n + i] = E[i].q;
    	for (int i = 1; i <= n + m; ++i)
    	{
    		isRoot[i] = true;
    		Father[i] = 0;
    		T[i] = i;
    	}
    	Ans = INF;
    	int t, CutE;
    	for (int i = 1; i <= m; ++i)
    	{
    		if (Find_Root(E[i].u) != Find_Root(E[i].v))
    		{
    			Link(E[i].u, n + i); Link(E[i].v, n + i);
    		}
    		else
    		{
    			Make_Root(E[i].u);
    			t = Access(E[i].v);
    			if (V[T[t]] > E[i].q)
    			{
    				CutE = T[t];
    				Cut(CutE, E[CutE - n].u); Cut(CutE, E[CutE - n].v);
    				Link(E[i].u, n + i); Link(E[i].v, n + i);
    			}
    		}
    		if (Find_Root(1) == Find_Root(n))
    		{
    			Make_Root(1);
    			t = Access(n);
    			Ans = gmin(Ans, E[i].p + V[T[t]]);
    		}
    	}
    	if (Ans == INF) Ans = -1;
    	printf("%d
    ", Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    WPF Popup弹出框箭头自动定位效果
    redis使用3
    linux常用命令
    Redis基础命令使用
    Redis使用笔记1
    jeesite常用注解记录
    Spring@Autowired注解与自动装配
    jeesite中的配置
    jeesite在eclipse中部署
    activiti流程连线与网关以及个人任务、组任务的指定方式
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4451249.html
Copyright © 2011-2022 走看看