[作业题] #6 CF571D


我这进度也太慢了吧,果然我整个人就是一个水。

Campus

题目描述

点此看题

解法

我自己想到正解的题都是水题,这题也不例外

考虑在并查集上修改的主要方法就是在根上打标记,那么本题我们就打标记,并且为了复杂度我们不下放,而是在询问的时候暴力跳父亲来计算标记的影响,前提是启发式合并保证深度是 \(O(\log n)\) 级别。

考虑清除操作(就是整个并查集赋 \(0\))的影响可以通过计算每个点最后一次被清除的时间得到,那么我们在每个节点上维护一个清除标记,在清楚时间 \(\geq\) 合并时间的条件下父亲的清除就可以作用于儿子,那么我们可以在线算出清除时间。

对于加法操作我们维护一个以时间为顺序的 \(\tt vector\),那么知道了清除时间之后就可以在上面二分,有贡献的加法标记一定是一段后缀(注意还要把合并时间也考虑进去),时间复杂度 \(O(n\log^2n)\)

总结

数据结构题,不一定要把东西全部维护出来询问直接去拿,询问操作可以承担起在线计算的作用。

观察操作的特性,比如本题清除操作的特性就是爹,清除之后什么都没有了,所以我们只需要知道最后一次清除的时间。

#include 
#include 
#include 
using namespace std;
const int M = 500005;
#define pii pair
#define x first
#define y second
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(ll x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,k,fa[2][M],t[2][M],sz[2][M],z[M];
vector d[M];vector s[M];
int find(int x,int o)
{
	return (x!=fa[o][x])?find(fa[o][x],o):x;
}
void merge(int u,int v,int o)
{
	u=find(u,o);v=find(v,o);
	if(u==v) return ;
	if(sz[o][u]

相关