P4230 连环病原体

一道 LCT 好题。

一种简单的 \(O(n^2)\) 就是枚举区间然后并查集看一下有没有成环。

注意到一个性质:如果 \([l,r]\) 内边已经成环,那么 \([l,r'],r' \in [r,n]\) 内边肯定都已成环。

据此,考虑设置两个指针 \(l,r\),对于每一个 \(l\) 求出对于其而言最小的 \(r\) 满足 \([l,r]\) 内边成环,而因为这样的 \(r\) 是满足单调不减性的所以可以类似双指针做。

因此现在问题就变成了加边,删边,判断成环,LCT 即可。

至于统计答案,设现在一个满足条件的区间为 \([l,r]\),则 \([l,r]\) 内所有点贡献是 \(m-r+1\)\([r+1,m]\) 内所有点贡献是一个首项为 \(m-r\),公差为 \(-1\) 的等差数列,两次差分分别维护即可。

实际上第一个差分是简单的,第二个差分是个二阶等差,也就是说打标记是打公差的标记,而做前缀和要做两遍,当然因为这道题的特殊性(右端点都是 \(m\))所以可以只做一次前缀和然后直接乘上 \(m-i+1\) 即可。

GitHub:CodeBase-of-Plozia

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P4230 连环病原体
	Date:2022/4/17
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 2e5 + 5, MAXM = 4e5 + 5;
int n, m;
LL sum1[MAXM], sum2[MAXM];
struct EDGE { int x, y; } Edge[MAXM];
struct LCT
{
	int fa, Son[2], tag;
}tree[MAXN];
#define fa(p) tree[p].fa
#define Son(p, x) tree[p].Son[x]
#define ls(p) tree[p].Son[0]
#define rs(p) tree[p].Son[1]
#define tag(p) tree[p].tag
#define RightSon(f, p) (rs(f) == p)
#define NotRoot(p) (ls(fa(p)) == p || rs(fa(p)) == p)

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
	return sum * fh;
}
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }
void Connect(int f, int x, int id) { fa(x) = f; Son(f, id) = x; }
void Spread(int p)
{
	if (!tag(p)) return ;
	if (ls(p)) { tag(ls(p)) ^= 1; std::swap(ls(ls(p)), rs(ls(p))); }
	if (rs(p)) { tag(rs(p)) ^= 1; std::swap(ls(rs(p)), rs(rs(p))); }
	tag(p) = 0;
}
void Rotate(int p)
{
	int f = fa(p), gf = fa(f), id = RightSon(f, p);
	Connect(f, Son(p, id ^ 1), id);
	fa(p) = gf; if (NotRoot(f)) Son(gf, RightSon(gf, f)) = p;
	Connect(p, f, id ^ 1);
}
void PushAll(int p) { if (NotRoot(p)) PushAll(fa(p)); Spread(p); }
void Splay(int p)
{
	PushAll(p);
	while (NotRoot(p))
	{
		int f = fa(p), gf = fa(f);
		if (NotRoot(f))
			if (RightSon(f, p) == RightSon(gf, f)) Rotate(f);
			else Rotate(p);
		Rotate(p);
	}
}

void Access(int p) { for (int x = 0; p; x = p, p = fa(p)) { Splay(p); rs(p) = x; } }
void MakeRoot(int p) { Access(p); Splay(p); tag(p) ^= 1; std::swap(ls(p), rs(p)); }
int FindRoot(int p)
{
	Access(p); Splay(p);
	while (ls(p)) { Spread(p); p = ls(p); }
	Splay(p); return p;
}
void Cut(int x, int y)
{
	MakeRoot(x);
	if (FindRoot(y) != x || fa(y) != x || ls(y)) return ;
	fa(y) = rs(x) = 0;
}
bool Link(int x, int y)
{
	MakeRoot(x);
	if (FindRoot(y) == x) return 0;
	fa(x) = y; return 1;
}

int main()
{
	m = Read();
	for (int i = 1; i <= m; ++i) { Edge[i].x = Read(), Edge[i].y = Read(); }
	int l = 1;
	for (int r = 1; r <= m; ++r)
	{
		while (!Link(Edge[r].x, Edge[r].y))
		{
			++sum1[m]; --sum1[r];
			sum2[l] += m - r + 1; sum2[r + 1] -= m - r + 1;
			Cut(Edge[l].x, Edge[l].y); ++l;
		}
	}
	for (int i = m - 1; i >= 1; --i) sum1[i] += sum1[i + 1];
	for (int i = 1; i <= m; ++i) sum2[i] += sum2[i - 1];
	for (int i = 1; i <= m; ++i) printf("%lld%c", sum2[i] + sum1[i] * (m - i + 1), " \n"[i == m]);
	return 0;
}
posted @ 2022-04-17 21:54  Plozia  阅读(30)  评论(0编辑  收藏  举报