天天看点

BZOJ4538 HNOI2016 网络

这道题解法很多,我知道三种:点分治;利用DFS序转化为平面内求最值问题;直接搞

这题考场上刚了很久,但是最终没有写出来,于是导致第一天雪崩,考试一定要冷静。

做的时候想到了前两种方法,因为第二种比较直观,于是选择了第二种。

但是发现这东西直接维护的话:

1.用3个树套在一起(堆可以用两个优先队列做出来,但是STL似乎很慢),很难写,加复杂度很吓人。

2.用KD-TREE,但是这东西我不熟练,复杂度也不会证明(@Claris说是根号M的)。

又发现题目没有要求强制在线,于是准备用CDQ分治+扫描线+线段树,貌似很容易就会想到这个思路。但写起来发现不是很好写,调了3个小时还没搞出来于是很不甘心,结果最终都没有做出来,浪费了大量的时间。

考完以后看网上的题解却发现考试的时候太naive忽视了最暴力最直观的解法。

插入一条线段,相当于在树上不在这个线段上的点都插入一个值。而线段可以轻松剖分成logN个区间,那么取补集,可以同样转化为logN个区间。那么再套上一个树套树(一个树可以用stl)就能直接修改了。

这种方法最慢的测试点是1.6秒,而TL是2秒。因为树链剖分的常数特别小才能通过,如果是前面的3个树套在一起肯定就过不了了。

该方法代码如下:

/*
* @Author: 逸闲
* @Date:   2016-04-19 15:24:06
* @Last Modified by:   逸闲
* @Last Modified time: 2016-04-19 16:12:03
*/

#include "cstdio"
#include "cstdlib"
#include "iostream"
#include "algorithm"
#include "cstring"
#include "queue"

using namespace std;

#define INF 0x3F3F3F3F
#define MAX_SIZE 200005
#define Eps
#define Mod
#define Get(x, a) (x ? x -> a : 0)
#define L(i) (i ? Mid + 1 : Left)
#define R(i) (i ? Right : Mid)
#define Travel(x) for(typeof(x.begin()) it = x.begin(); it != x.end(); ++it)

inline int Get_Int()
{
	int Num = 0, Flag = 1;
	char ch;
	do
	{
		ch = getchar();
		if(ch == '-')
			Flag = -Flag;
	}
	while(ch < '0' || ch > '9');
	do
	{
		Num = Num * 10 + ch - '0';
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9');
	return Num * Flag;
}

class Heap
{
public:
	priority_queue<int> Exist, Deleted;

	inline void Push(int Value)
	{
		Exist.push(Value);
	}

	inline void Erase(int Value)
	{
		Deleted.push(Value);
	}

	inline int Top()
	{
		while(!Deleted.empty() && Exist.top() == Deleted.top())
		{
			Exist.pop();
			Deleted.pop();
		}
		if(Exist.empty())
			return -1;
		else
			return Exist.top();
	}
};

namespace Segment_Tree
{
	Heap Max[MAX_SIZE * 4];

	void Modify(int Now, int left, int right, int Value, int Flag, int Left, int Right)
	{
		if(left == Left && right == Right)
		{
			if(!Flag)
				Max[Now].Push(Value);
			else
				Max[Now].Erase(Value);
			return;
		}
		int Mid = Left + Right >> 1;
		if(left > Mid || right <= Mid)
		{
			int i = left > Mid;
			Modify(Now << 1 | i, left, right, Value, Flag, L(i), R(i));
			return;
		}
		Modify(Now << 1, left, Mid, Value, Flag, L(0), R(0));
		Modify(Now << 1 | 1, Mid + 1, right, Value, Flag, L(1), R(1));
	}

	int Query(int Now, int Position, int Left, int Right)
	{
		int Ans = Max[Now].Top();
		if(Left != Right)
		{
			int Mid = Left + Right >> 1, i = Position > Mid;
			Ans = max(Ans, Query(Now << 1 | i, Position, L(i), R(i)));
		}
		return Ans;
	}
}

class Edge
{
public:
	int To, Next;
}Edges[MAX_SIZE * 2];

int N, Q, Total;
int Front[MAX_SIZE], A[MAX_SIZE], B[MAX_SIZE], C[MAX_SIZE];

inline void Add_Edge(int From, int To)
{
	Edges[++Total].To = To;
	Edges[Total].Next = Front[From];
	Front[From] = Total;
}

inline void Add_Edges(int From, int To)
{
	Add_Edge(From, To);
	Add_Edge(To, From);
}

vector< pair<int, int> > temp;

namespace Heavy_Light_Decomposition
{
	int Total;
	int Father[MAX_SIZE], Size[MAX_SIZE], Son[MAX_SIZE], Top[MAX_SIZE], DFN[MAX_SIZE], Position[MAX_SIZE], Depth[MAX_SIZE];

	void DFS1(int Now)
	{
		Size[Now] = 1;
		Depth[Now] = Depth[Father[Now]] + 1;
		for(int i = Front[Now]; i; i = Edges[i].Next)
			if(Edges[i].To != Father[Now])
			{
				Father[Edges[i].To] = Now;
				DFS1(Edges[i].To);
				if(Size[Edges[i].To] > Size[Son[Now]])
					Son[Now] = Edges[i].To;
				Size[Now] += Size[Edges[i].To];
			}
	}

	void DFS2(int Now, int top)
	{
		Top[Now] = top;
		DFN[Now] = ++Total;
		Position[Total] = Now;
		if(Son[Now])
			DFS2(Son[Now], top);
		for(int i = Front[Now]; i; i = Edges[i].Next)
			if(Edges[i].To != Father[Now] && Edges[i].To != Son[Now])
				DFS2(Edges[i].To, Edges[i].To);
	}

	inline void Build()
	{
		DFS1(1);
		DFS2(1, 1);
	}

	inline void Get_Interval(int x, int y)
	{
		while(Top[x] != Top[y])
		{
			if(Depth[Top[x]] < Depth[Top[y]])
				swap(x, y);
			temp.push_back(make_pair(DFN[Top[x]], DFN[x]));
			x = Father[Top[x]];
		}
		if(Depth[x] < Depth[y])
			swap(x, y);
		temp.push_back(make_pair(DFN[y], DFN[x]));
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("network.in", "r", stdin);
	freopen("network.out", "w", stdout);
#endif
	cin >> N >> Q;
	for(int i = 1; i < N; ++i)
		Add_Edges(Get_Int(), Get_Int());
	Heavy_Light_Decomposition::Build();
	for(int i = 1; i <= Q; ++i)
	{
		int Op = Get_Int();
		if(Op == 2)
			printf("%d\n", Segment_Tree::Query(1, Heavy_Light_Decomposition::DFN[Get_Int()], 1, N));
		else
		{
			int x, y, v;
			if(Op == 0)
			{
				A[i] = x = Get_Int();
				B[i] = y = Get_Int();
				C[i] = v = Get_Int();
			}
			else
			{
				int k = Get_Int();
				x = A[k];
				y = B[k];
				v = C[k];
			}
			temp.clear();
			Heavy_Light_Decomposition::Get_Interval(x, y);
			sort(temp.begin(), temp.end());
			int Last = 1;
			Travel(temp)
			{
				if(it -> first > Last)
					Segment_Tree::Modify(1, Last, it -> first - 1, v, Op, 1, N);
				Last = it -> second + 1;
			}
			if(Last <= N)
				Segment_Tree::Modify(1, Last, N, v, Op, 1, N);
		}
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}
           

PS:HLD好叼啊

继续阅读