• 启智树提高组Day2T2 涛涛的集合


    支持四种操作:集合合并,集合加,(原序列上)区间清零,单点查询。其中单点查询有 (10^7) 次,其余 (50000) 次。(n le 2 imes 10^5)

    显然要用到并查集。但是怎么维护原序列上区间清零呢?这需要用到一个小技巧:维护每个点最后被清零的时间,查询就是当前的值减去最后被清零的值。这个可以用分块来维护。于是,单点查询 ( imes 2),区间清零 (=0)

    然后我们需要用一个能够快速 (O(1)) 查并查集的祖先节点,还要支持打集合加标记的并查集。这个可以启发式合并,集合 -> vector,合并的时候直接把小的那个集合的元素全部拿出来改掉信息,下放标记,扔到大的那个里面。注意为了适应大的集合,那些元素可能还要减去大的集合的加法标记。

    总复杂度:(O(q_0sqrt n + nlogn + q_1))

    inline void Pushdown(int cur) {
    	if (tags[cur]) {
    		for (register int i = st[cur]; i <= ed[cur]; ++i)
    			val[i] = tags[cur];
    		tags[cur] = 0;
    	}
    }
    inline void modify(int l, int r, int v) {
    	if (blong[l] == blong[r]) {
    		Pushdown(blong[l]);
    		for (register int i = l; i <= r; ++i)	val[i] = v;
    		return ;
    	}
    	Pushdown(blong[l]); for (register int i = l; i <= ed[blong[l]]; ++i)	val[i] = v;
    	Pushdown(blong[r]); for (register int i = r; i >= st[blong[r]]; --i)	val[i] = v;
    	for (register int i = blong[l] + 1; i < blong[r]; ++i)
    		tags[i] = v;
    }
    int opt[N], X[N], Y[N];
    vector<pair<PII, int> > vt[N];
    int main() {
    	read(n), read(q);
    	for (register int i = 1; i <= n; ++i)	anc[i] = i, vec[i].push_back(i);
    	init();
    	for (register int i = 1; i <= q; ++i) {
    		read(opt[i]); read(X[i]), read(Y[i]);
    		if (opt[i] == 3) {
    			modify(X[i], Y[i], i);
    		} else if (opt[i] == 4) {
    			for (register int j = X[i]; j <= Y[i]; ++j) {
    				int lst = tags[blong[j]] ? tags[blong[j]] : val[j];
    				vt[i].push_back(MP(MP(i, j), 1));
    				vt[lst].push_back(MP(MP(i, j), -1));
    			}
    		}
    	}
    	memset(val, 0, sizeof(val));
    	for (register int i = 1; i <= q; ++i) {
    		if (opt[i] == 1) {
    			X[i] = anc[X[i]]; Y[i] = anc[Y[i]];
    			if (X[i] != Y[i]) {
    				if (vec[X[i]].size() > vec[Y[i]].size()) swap(X[i], Y[i]);
    				for (register unsigned int j = 0; j < vec[X[i]].size(); ++j) {
    					int cur = vec[X[i]][j];
    					anc[cur] = Y[i];
    					vec[Y[i]].push_back(cur);
    					val[cur] += taga[X[i]] - taga[Y[i]];
    				}
    				vec[X[i]].clear();
    			}
    		} else if (opt[i] == 2) {
    			X[i] = anc[X[i]];
    			taga[X[i]] += Y[i];
    		}
    		for (register unsigned int j = 0; j < vt[i].size(); ++j) {
    			int id = vt[i][j].first.first, type = vt[i][j].second;
    			int cur = vt[i][j].first.second;
    			ans[id] += type * (val[cur] + taga[anc[cur]]);
    		}
    	}
    	for (register int i = 1; i <= q; ++i)
    		if (opt[i] == 4)
    			printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    请教visiouml活动图中动作状态和状态的区别谢谢 软件工程管理 软件规划版
    !!!IT人员迅速提升自我效率的十大方法
    如何在Visio的UML活动图中(判断及控制流)添加文字啊????
    Convert Standard String to System::String
    m_pRecordset遍历记录集之后,m_pRecordsetMoveFirst()为什么会出错
    !!! C++/CLI中使用using namespace System::Windows::Forms;+MessageBox报错最重要的是看MSDN 每个函数的使用例子
    !!!创建 UML 活动图 Visio Office_com
    C#反射之AssemblyCLR/c++可以通过反射调用c#类库
    请允许我进入你的世界
    以赛庆党日
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13569296.html
Copyright © 2020-2023  润新知