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;
}

發佈了98 篇原創文章 · 獲贊 26 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章