【CQBZOJ - 3611】作业斯的监视

版权声明:本文为博主原创文章……懂吗?要尊重别人的劳动成果呐 https://blog.csdn.net/Tiw_Air_Op1721/article/details/84102708

@作业斯的监视@


@题目描述@

数学的期中考试前一天晚上,蒟蒻的YHN在教室里上晚自习。但他过于无聊于是沉迷竞赛。正当YHN同学在颓废时,虗神附体,他YY出来一个很奇葩的算法。YHN同学非常激动,想立刻把这个算法给在教室另一端的㲚同学讲。
25班的教室布局非常奇特。有n个座位,在这些座位间有m条无向边。
然而,作业斯正在讲台上坐着!她正在监视某些边,使得YHN不敢从那些边经过(视为那些边断开)。
YHN同学为了能够到达㲚同学的座位,需要询问q次
每次询问需要得到:此时教室的联通块数量,以及每个联通块的大小的异或和。

输入
第一行,给出三个数:n、m、q,分别表示桌子的数量,边的数量,以及询问的次数。
接下来m行,每行两个值u,v表示有一条在u,v之间的无向边。(不保证没有重边)
接下来q行,每行两个值l,r表示作业斯正在监视编号在[1,l-1]∪[r+1,m]范围内的所有边。
数据规模: n<=10000,m,q<=50000
输出
总计q行,每行输出两个值,分别表示当前那次询问时的联通块数量,以及每个联通块大小的异或和。

样例输入
4 6 3
1 2
2 3
3 4
1 3
2 4
1 4
1 3
2 5
3 4
样例输出
1 4
1 4
2 2

@题解@

回滚莫队的模板题。
膜拜 yhn dalao,自己发明出了回滚莫队算法。 orz
https://blog.csdn.net/qq_34454069/article/details/80184286 【yhn dalao 的博客】

讲讲一下回滚莫队的原理:
某些情况下使用莫队,可能会遇到加入信息很简单,但是删除信息很困难(对应到区间上就是区间端点往外扩很简单,但是往内缩很困难)。
于是我们就需要优化莫队的实现,使其可以解决这一类问题。

具体来说:对于每一块,首先清空所有的信息。如果左右端点位于同一个块,暴力搞即可;否则按右端点递增排序,这样右端点的变化就只剩往外扩的,然后对于每一个左端点,从这个块的右端点往内移,统计信息计算答案。
回滚莫队的最大特色是:将左端点的变化量加入栈,统计完答案后,将栈中的信息还原。
注意是还原!

对于这道题来说,我们仅需要在区间往外扩的时候维护一个并查集即可。但是为了方便回滚莫队还原信息,这个并查集不能使用路径压缩,只能用启发式合并。

@代码@

想当初我研究回滚莫队研究了一整个上午……为自己心累qwq……
实现上……我选择了让我最容易理解的一种写法,大家可以自己看看代码细节吧,如果有什么问题留言在下面询问我qwq。

#include<stack>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
const int MAXM = 50000;
const int MAXQ = 50000;
const int BLOSIZ = 233;
typedef pair<int, int> pii;
int fa[MAXN + 5], siz[MAXN + 5], rnk[MAXN + 5];
int Find(int x) {
	return fa[x] == x ? x : Find(fa[x]);
}
struct query{
	int l, r, num;
}qry[MAXQ + 5], tmp[MAXQ + 5];
bool cmp_l(query a, query b) {
	if( a.l == b.l ) return a.r < b.r;
	else return a.l < b.l;
}
bool cmp_r(query a, query b) {
	if( a.r == b.r ) return a.l < b.l;
	else return a.r < b.r;
}
struct edge{
	int u, v;
}edges[MAXM + 5];
pii ans[MAXQ + 5], res;
int le, ri;
int n, m, q;
void init() {
	for(int i=1;i<=n;i++)
		fa[i] = i, siz[i] = 1, rnk[i] = 1;
	res = make_pair(n, n&1);
}
struct change{
	int pos, rnk, fa, siz;
	change(int _p, int _r, int _f, int _s):pos(_p), rnk(_r), fa(_f), siz(_s){};
};
stack<change>stk;
void restore() {
	while( !stk.empty() ) {
		change t = stk.top(); stk.pop();
		siz[t.pos] = t.siz, rnk[t.pos] = t.rnk,	fa[t.pos] = t.fa;
	}
}
void add(int x, int type) {
	int u = Find(edges[x].u), v = Find(edges[x].v);
	if( u == v ) return ;
	res.first--; res.second = res.second^siz[u]^siz[v]^(siz[u]+siz[v]);
	if( rnk[u] > rnk[v] ) swap(u, v);
	if( type ) {
		stk.push(change(u, rnk[u], fa[u], siz[u]));
		stk.push(change(v, rnk[v], fa[v], siz[v]));
	}
	fa[u] = v, siz[v] += siz[u];
	if( rnk[u] == rnk[v] ) rnk[v]++;
}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	for(int i=1;i<=m;i++)
		scanf("%d%d", &edges[i].u, &edges[i].v);
	for(int i=1;i<=q;i++) {
		scanf("%d%d", &qry[i].l, &qry[i].r);
		qry[i].num = i;
	}
	sort(qry+1, qry+q+1, cmp_l);
	int qcnt = 1, br = BLOSIZ;
	while( qcnt <= q ) {
		init(); int tcnt = 0;
		if( br > m ) br = m; ri = br;
		while( qcnt <= q && qry[qcnt].l <= br )
			tmp[++tcnt] = qry[qcnt++];
		sort(tmp+1, tmp+tcnt+1, cmp_r);
		for(int i=1;i<=tcnt;i++) {
			while( ri < tmp[i].r ) add(++ri, 0);
			pii tres = res;
			if( tmp[i].r <= br ) le = tmp[i].r;
			else le = br;
			while( le >= tmp[i].l ) add(le--, 1);
			ans[tmp[i].num] = res;
			res = tres; restore();
		}
		br += BLOSIZ;
	}
	for(int i=1;i<=q;i++)
		printf("%d %d\n", ans[i].first, ans[i].second);
}

@END@

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。

扫描二维码关注公众号,回复: 4104755 查看本文章

猜你喜欢

转载自blog.csdn.net/Tiw_Air_Op1721/article/details/84102708