bzoj 4358 permu(回滚莫队(不删除莫队) + 并查集)

在这里插入图片描述


显然可以莫队维护权值建线段树最大连续子段长度,复杂度是 n n log n n \sqrt n \log n 5 1 0 4 5*10^4 的数据下 n n n \sqrt n 大概为 1 0 7 10^7 ,再加一个 log 和大常数不太容易卡过去(有巨佬卡过去了)

考虑用并查集维护每个权值最左延申 L [ i ] L[i] 和最右延申 R [ i ] R[i] ,答案就是 m a x ( R [ i ] L [ i ] 1 ) max(R[i] - L[i] - 1) ,在莫队的过程中需要维护最大值,由于最大值不支持删除操作,需要用回滚莫队(即不删除莫队)

回滚莫队:
同一个块内的询问右边界是单调递增的,左边界是乱序但权值变换在 n \sqrt n 以内。
考虑每次询问前的 左边界先移到 这个块的右端点,每次将左边界移到询问左边界,询问结束将左边界撤回到块的右端点,中间的操作撤回。这样整个过程就只有插入操作没有删除操作。

需要分三类情况讨论:
1.询问的右边界和左边界在同一个块:如果左边界从块的右端点移到询问左边界,可能会维护不在询问区间内的信息,但此时区间长度小于 n \sqrt n 因此直接暴力即可,询问完将维护的信息全部清空。

2.满足1的情况下,询问的左边界和上一个的询问的左边界不在同一个块:这种清空由于 询问右边界 可能减小了,而右边界的操作无法撤回,因此只能暴力重新维护,询问结束后将左边界回退到块的右端点,由于不同的块只有 n \sqrt n 个,这种清空最多发生 n \sqrt n 次,复杂度为 n n n \sqrt n

3.满足1的情况下,询问的左边界和上一个询问的左边界在同一个块:先将右边界移到询问右边界,接着将左边界移到询问左边界,得到答案后左边界移回到块右端点。

总体复杂度: n n n \sqrt n


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
#define pii pair<int,int>
#define fir first
#define sec second
int block,n,m,a[maxn],curleft,curright,ans[maxn],L[maxn],R[maxn],res;
inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
struct ss{
    int id,l,r;
    ss(int x = 0,int y = 0,int z = 0) {
        id = x; l = y; r = z;
    }
    bool operator < (const ss &rhs) const {
        return l / block + (l % block > 0) == rhs.l / block + (rhs.l % block > 0) ? r < rhs.r : l < rhs.l;
    } 
    //排序的分块方式和下面讨论的方式必须相同
}q[maxn];
int findL(int x) {
	return x == L[x] ? x : L[x] = findL(L[x]);
}
int findR(int x) {
	return x == R[x] ? x : R[x] = findR(R[x]);
}
void modify(int l,int r,int id) {
	int pos = min((l / block + (l % block > 0)) * block,n);
	if(r <= pos) {
		res = 0;
		for(int i = curleft; i <= curright; i++)					//清空上一次,这种情况同样只会发生 根号 n 次 
    		L[a[i]] = R[a[i]] = a[i];
    	for(int i = l; i <= r; i++) {
    		L[a[i]] = a[i] - 1;
    		R[a[i]] = a[i] + 1;
    		res = max(res,findR(a[i]) - findL(a[i]) - 1);
		}
		ans[id] = res;
		for(int i = l; i <= r; i++)
			L[a[i]] = R[a[i]] = a[i];
		curleft = curright = pos;
		L[a[pos]] = a[pos] - 1;
		R[a[pos]] = a[pos] + 1;
		res = 1;
	} else {
		if(l / block + (l % block > 0)  != curleft / block + (curleft % block > 0)) {			// 不同块只会发生 根号 n 次	
			int tpans = 0;	
			res = 0;
			for(int i = curleft; i <= curright; i++)					//清空上一次 
	    		L[a[i]] = R[a[i]] = a[i];
	    	for(int i = pos; i <= r; i++) {
	    		L[a[i]] = a[i] - 1;
	    		R[a[i]] = a[i] + 1;
	    		res = max(res,findR(a[i]) - findL(a[i]) - 1);
			}
			tpans = res;
			for(int i = pos - 1; i >= l; i--) {
				L[a[i]] = a[i] - 1;
	    		R[a[i]] = a[i] + 1;
	    		res = max(res,findR(a[i]) - findL(a[i]) - 1);
			}
			for(int i = l; i < pos; i++)
				L[a[i]] = a[i], R[a[i]] = a[i];
			ans[id] = res;
			res = tpans;
			curleft = pos,curright = r;
		} else {
			int tpans = 0;
			while(curright < r) {
				curright++;
				L[a[curright]] = a[curright] - 1;
				R[a[curright]] = a[curright] + 1;
				res = max(res,findR(a[curright]) - findL(a[curright]) - 1);
			}
			tpans = res;
			for(int i = pos - 1; i >= l; i--) {
				L[a[i]] = a[i] - 1;
	    		R[a[i]] = a[i] + 1;
	    		res = max(res,findR(a[i]) - findL(a[i]) - 1);
			}
			for(int i = l; i < pos; i++)
				L[a[i]] = a[i], R[a[i]] = a[i];
			ans[id] = res;
			res = tpans;
			curleft = pos,curright = r;
		}		
	}	
}
int main() {
    n = read(); m = read();
    for(int i = 1; i <= n; i++)
        a[i] = read();
    for(int i = 1,l,r; i <= m; i++) {
        l = read(); r = read();
        q[i] = ss(i,l,r);
    }
    block = sqrt(n);
    sort(q + 1,q + m + 1);
    res = curleft = curright = 0;
    for(int i = 0; i <= n + 1; i++)
    	L[i] = R[i] = i;
    for(int i = 1; i <= m;i++) {
        modify(q[i].l,q[i].r,q[i].id);
    }
    for(int i = 1; i <= m; i++)
        printf("%d\n",ans[i]);
    return 0;
}
发布了332 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/104111225