BZOJ1858 序列操作 [treap,避免双标记的特殊技巧]

题意:给一个01序列,有5种操作:
0 L R 将[L,R]之间的数字都变成0;
1 L R 将[L,R]之间的数字都变成1;
2 L R 将[L R]之间的数字都取反;
3 L R 询问[L R]之间1的个数;

4 L R 询问[L,R]之间连续1的个数最大是多少.

第一眼:这sb题。。

第二眼:这sb题。。

第三眼:哎呀卧槽这儿取反和赋值俩标记咋搞???

大致脑补了一下,很不确定对不对,然后上网看了下别人的写法,也觉得很难想清楚。。

管他的,开始写吧!!

用啥数据结构?线段树?貌似很好写,但是线段树修改都还好,最要命的是询问的时候你还得拼凑答案。

那就treap吧。。treap可以split出来直接读取答案。

写了差不多40分钟终于写出来了,样例1A了。然后往codevs上叫只过了一个点。。

然后就开始2+hrs的Debug。。然后实在找不出错了。。然后就打算另辟蹊径,我不维护两个标记了,我直接维护两个treap!!空间没限制就是任性!!这两个treap每一位都相反,比如说第一个treap是01001,第二个就是10110,各自维护对应的信息,分开保存根。

每次题目要求<0 l r>操作的时候实际对第一个treap进行<0 l r>,对第二个进行<1 l r>;

操作<1 l r>类似;

每次题目要求<2 l r>操作的时候交换两个treap的[l,r]区间即可。

然后就只需要保存一个same标记啦!!!

然后就慢死了。。。常数略大。。。bzoj上跑了9s+。。

但是我觉得这种思路真的很实用!!!像这道题这样按位取反,还有经典的区间翻转都能用这种维护两个序列的方式来实现,从而避免多个懒标记下传顺序的讨论。更准确的说,如果一个操作连续对[l,r]作用两次后会恢复原状,就可以用这种方法。虽然牺牲一点常数时间,但是准确率大大提高了。。这种方法treap和splay都可以实现,但好像线段树不好做。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cassert>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
typedef pair<int, int> pii;
const int MAXN = 100005*2;
#define fi first
#define se second
int N, M;

inline int ran()
{
	static int sd = 1237;
	return sd = (sd*12371237)&0x7fffffff;
}
struct Treap
{
	int lch[MAXN], rch[MAXN], sz[MAXN], fix[MAXN];
	int xt[MAXN], cnt[MAXN], val[MAXN];
	int lx[MAXN][2], rx[MAXN][2], mx[MAXN][2];
	int ncnt, r1, r2; //建两棵相反的treap来避免reverse标记

	int L, R, flag;
	inline void pushup(int x)
	{
		L = lch[x], R = rch[x];
		sz[x] = sz[L] + sz[R] + 1;
		cnt[x] = cnt[L] + cnt[R] + val[x];
		rep(c, 0, 1)
		{
			flag = (val[x]==c);
			if (mx[L][c] == sz[L]) lx[x][c] = sz[L] + flag + lx[R][c]*flag;
			else lx[x][c] = lx[L][c];
			if (mx[R][c] == sz[R]) rx[x][c] = sz[R] + flag + rx[L][c]*flag;
			else rx[x][c] = rx[R][c];
			mx[x][c] = max(mx[L][c], mx[R][c]);
			if (flag) mx[x][c] = max(mx[x][c], rx[L][c]+1+lx[R][c]);
		}
	}

	inline void upxt(int x, int v)
	{
		if (!x) return;
		cnt[x] = (val[x]=v) * sz[x];
		lx[x][v] = rx[x][v] = mx[x][v] = sz[x];
		lx[x][!v] = rx[x][!v] = mx[x][!v] = 0;
		xt[x] = v;
	}

	inline void pushdown(int x)
	{
		L = lch[x], R = rch[x];
		if (~xt[x])
		{
			upxt(L, xt[x]), upxt(R, xt[x]);
			xt[x] = -1;
		}
	}

	int NewNode(int i)
	{
		++ncnt;
		fix[ncnt] = ran();
		cnt[ncnt] = val[ncnt] = i;
		sz[ncnt] = 1;
		lx[ncnt][i] = rx[ncnt][i] = mx[ncnt][i] = 1;
		xt[ncnt] = -1;
		return ncnt;
	}

	int merge(int a, int b)
	{
		if (!a || !b) return a|b;
		pushdown(a), pushdown(b);
		if (fix[a] > fix[b])
		{
			rch[a] = merge(rch[a], b);
			return pushup(a), a;
		}
		else
		{
			lch[b] = merge(a, lch[b]);
			return pushup(b), b;
		}
	}

	pii split(int x, int k)
	{
		if (!x) return pii(0, 0);
		pushdown(x);
		pii y;
		if (sz[lch[x]] >= k)
		{
			y = split(lch[x], k);
			lch[x] = y.se, y.se = x;
		}
		else
		{
			y = split(rch[x], k - sz[lch[x]] - 1);
			rch[x] = y.fi, y.fi = x;
		}
		return pushup(x), y;
	}

	int sta[MAXN];
	int build(int*a, int n)
	{
		int x, las, p = 0;
		rep(i, 1, n)
		{
			x = NewNode(a[i]);
			las = 0;
			while (p && fix[sta[p]] < fix[x])
				pushup(las = sta[p]), sta[p--] = 0;
			if (p) rch[sta[p]] = x;
			lch[x] = las;
			sta[++p] = x;
		}
		while (p) pushup(sta[p--]);
		return sta[1];
	}

	void init(int*a, int n)
	{
		r1 = build(a, n);
		rep(i, 1, n) a[i] ^= 1;
		r2 = build(a, n);
	}

	void makesame(int l, int r, int v)
	{
		pii t1 = split(r1, l-1);
		pii t2 = split(t1.se, r-l+1);
		upxt(t2.fi, v);
		r1 = merge(merge(t1.fi, t2.fi), t2.se);

		t1 = split(r2, l-1);
		t2 = split(t1.se, r-l+1);
		upxt(t2.fi, !v);
		r2 = merge(merge(t1.fi, t2.fi), t2.se);
	}

	void reverse(int l, int r)
	{
		pii t1 = split(r1, l-1);
		pii t2 = split(t1.se, r-l+1);
		pii t3 = split(r2, l-1);
		pii t4 = split(t3.se, r-l+1);
		r1 = merge(merge(t1.fi, t4.fi), t2.se);
		r2 = merge(merge(t3.fi, t2.fi), t4.se);
	}

	int qcnt(int l, int r)
	{
		pii t1 = split(r1, l-1);
		pii t2 = split(t1.se, r-l+1);
		int ans = cnt[t2.fi];
		r1 = merge(merge(t1.fi, t2.fi), t2.se);
		return ans;
	}

	int qmx(int l, int r)
	{
		pii t1 = split(r1, l-1);
		pii t2 = split(t1.se, r-l+1);
		int ans = mx[t2.fi][1];
		r1 = merge(merge(t1.fi, t2.fi), t2.se);
		return ans;
	}
} tp;

int s[MAXN];
int main()
{
	freopen("data.txt", "r", stdin);
	freopen("f.out", "w", stdout);
	scanf("%d%d", &N, &M);
	rep(i, 1, N) scanf("%d", s+i);
	tp.init(s, N);
	int op, l, r;
	while (M --)
	{
		scanf("%d%d%d", &op, &l, &r);
		l++, r++;
		switch(op)
		{
			case 0: tp.makesame(l, r, 0); break;
			case 1: tp.makesame(l, r, 1); break;
			case 2: tp.reverse(l, r); break;
			case 3: printf("%d\n", tp.qcnt(l, r)); break;
			default:printf("%d\n", tp.qmx(l, r)); break;
		}
	}
	return 0;
}

你可能感兴趣的:(BZOJ1858 序列操作 [treap,避免双标记的特殊技巧])