• Luogu3379 【模板】最近公共祖先(LCA)


    题面

    题解

    这里讲一种硬核做法。

    首先(mathrm{dfs})整棵树,求出这棵树的欧拉序,然后(mathrm{LCA})问题就变成了(pm 1mathrm{RMQ})问题。

    考虑(mathrm{O}(n))解决(pm 1mathrm{RMQ})问题。

    将原序列分块,每一块长度为(dfrac {log_2 n}2),块外用(mathrm{ST})表预处理,复杂度(mathrm{O}(n)),考虑块内如何(mathrm{O}(1))回答。

    因为相邻两项之差最多为(1),所以块内本质不同的状态只有(2 ^ {frac {log n} 2} = sqrt n)种。

    那么可以设(f[S][l][r])表示状态为(S)时,区间([l, r])的最小值。

    于是块内就能(mathrm{O}(1))解决了,这一部分预处理的复杂度为(mathrm{O}(sqrt n log^2n))

    因为以上操作复杂度均没有超过(mathrm{O}(n)),所以预处理的复杂度为(mathrm{O}(n)),总复杂度为(mathrm{O}(n) - mathrm{O}(1))

    代码

    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    
    inline int read()
    {
    	int data = 0, w = 1; char ch = getchar();
    	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    	if (ch == '-') w = -1, ch = getchar();
    	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
    	return data * w;
    }
    
    const int maxn(500010), LEN(1050), BLK(10), N(200010);
    struct edge { int next, to; } e[maxn << 1];
    int head[maxn], e_num, n, m, S, dep[maxn], f[1 << BLK][BLK][BLK];
    int A[maxn << 1], ST[BLK << 1][N], Log[maxn << 1], pos[maxn], cnt;
    int Len, Blk, minv[N], set[N], B[maxn << 1];
    inline int min(const int &x, const int &y) { return B[x] < B[y] ? x : y; }
    inline int Pos(const int &x) { return (x - 1) / Len + 1; }
    inline int Posy(const int &x) { return (x - 1) % Len; }
    inline void add_edge(int from, int to)
    {
    	e[++e_num] = (edge) {head[from], to};
    	head[from] = e_num;
    }
    
    void dfs(int x, int fa)
    {
    	A[pos[x] = ++cnt] = x, B[cnt] = dep[x];
    	for (int i = head[x]; i; i = e[i].next)
    	{
    		int to = e[i].to; if (to == fa) continue;
    		dep[to] = dep[x] + 1, dfs(to, x), A[++cnt] = x, B[cnt] = dep[x];
    	}
    }
    
    void Init()
    {
    	Len = std::max(1, (int) (log(cnt * 1.) / log(2.) * .5));
    	Blk = cnt / Len + (cnt % Len > 0); int SIZ = 1 << (Len - 1);
    	for (int i = 2; i <= cnt; i++) Log[i] = Log[i >> 1] + 1;
    	for (int i = 0; i < SIZ; i++) for (int l = 0; l < Len; l++)
    		for (int r = (f[i][l][l] = l) + 1, now = 0, _min = 0; r < Len; r++)
    		{
    			f[i][l][r] = f[i][l][r - 1];
    			if (i & (1 << (r - 1))) ++now;
    			else { --now; if(now < _min) _min = now, f[i][l][r] = r; }
    		}
    	for (int i = 1; i <= cnt; i++)
    		if (!Posy(i)) minv[Pos(i)] = i, set[Pos(i)] = 0;
    		else
    		{
    			if (B[i] < B[minv[Pos(i)]]) minv[Pos(i)] = i;
    			if (B[i] > B[i - 1]) set[Pos(i)] |= 1 << (Posy(i) - 1);
    		}
    	for (int i = 1; i <= Blk; i++) ST[0][i] = minv[i];
    	for (int i = 1; i <= Log[Blk]; i++)
    		for (int j = 1; j <= Blk - (1 << i) + 1; j++)
    			ST[i][j] = min(ST[i - 1][j], ST[i - 1][j + (1 << (i - 1))]);
    }
    
    int Query(int l, int r)
    {
    	l = pos[l], r = pos[r]; if(l > r) std::swap(l, r);
    	int idl = Pos(l), idr = Pos(r);
    	if (idl == idr) return (idl - 1) * Len + f[set[idl]][Posy(l)][Posy(r)] + 1;
    	else
    	{
    		int a1 = (idl - 1) * Len + f[set[idl]][Posy(l)][Len - 1] + 1;
    		int a2 = (idr - 1) * Len + f[set[idr]][0][Posy(r)] + 1;
    		int ans = min(a1, a2), _l = Log[idr - idl - 1];
    		if (idr - idl - 1)
    			return min(ans, min(ST[_l][idl + 1], ST[_l][idr - (1 << _l)]));
    		return ans;
    	}
    }
    
    int main()
    {
    	n = read(), m = read(), S = read();
    	for (int i = 1, a, b; i < n; i++)
    		a = read(), b = read(), add_edge(a, b), add_edge(b, a);
    	dep[S] = 1, dfs(S, 0); Init();
    	for (int a, b; m--; ) a = read(), b = read(), printf("%d
    ", A[Query(a, b)]);
    	return 0;
    }
    
  • 相关阅读:
    课堂作业之公文流转
    统计字符出现频率(java)
    课堂测试第八周
    HTML学习笔记——语法+骨架
    HTTP协议
    MVC架构模式概述
    CodeIgniter框架——CI中视图路径问题
    CodeIgniter框架——CI组件间信息流走向
    CodeIgniter框架——数据库类(配置+快速入门)
    chm文件无法阅读
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/11142232.html
Copyright © 2020-2023  润新知