CF1340F Nastya and CBS

首先考虑如何维护一段括号序列。

根据观察可知,对一个括号序列来说,若存在这种形式 '{ )' 则包含该区段的询问均不合法,反之不存在则说明可能构成合法的解。这启发我们可以将一个段都进行这样的缩区间,然后对于一个询问将其全部拼在一起,在拼的时候考虑一下是否满足上述情况。

对于的判断两个括号序列是否相同时采用 Hash 维护,具体来说维护一个左向开始的 Hash 和右向开始的 Hash,同种括号不同方向用绝对值即可

针对上述解法,分块无疑是最好写的,这也是大多数题解采用的方法,,复杂度为 \(O(n\sqrt n)\) (这里默认 \(n\)\(q\) 同阶

这里给出一种官方题解做法:线段树套可持久化文艺平衡树,时间复杂度是 \(O(n \log^2n)\),空间复杂度是 \(O(n\log n + q\log n)\)。并且因为我太菜了,不会随时回收可持久化平衡树的节点,所以是每 \(10^4\) 次扫一遍线段树找出所有废弃节点,回收再利用。(用可持久化平衡树实现细节还挺多的,还是分块好写

虽然看起来是这玩意复杂度更优,但它常数实在是太大了……

#include<bits/stdc++.h>

using namespace std;

#define INF 1ll<<30
#define ill unsigned int

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}
const int np = 1e5 + 5;
template<typename _T>
inline _T Abs(_T &x)
{
	return x < 0?-x:x;
}
int pare[np];
const int qt = 90;
int ch[2][qt * np],pri[np * qt],val[np * qt];
ill Hashl[np * qt],Hashr[np * qt];
const ill base = 29;
bitset<np * 90> use;
ill p_[np];
int siz[np * qt],tail = 1;//,cnt;//,tail;//,rot[23333]cnt;
#define ls(x) (ch[0][x])
#define rs(x) (ch[1][x])

inline int Rub()
{
	while(use[tail]) tail++;
	return tail++;
}

inline int New(int a)
{
//	++cnt;
	int cnt;
	cnt = Rub();
	val[cnt] = a;
	Hashl[cnt] = Hashr[cnt] = Abs(a);
	ls(cnt) =rs(cnt) = 0;
	pri[cnt] = rand();
	siz[cnt] = 1;
	return cnt;
}

inline void cop(int x,int y)
{
	ls(x) = ls(y),rs(x) = rs(y);
	pri[x] = pri[y],val[x] = val[y];
	Hashl[x] = Hashl[y],Hashr[x] = Hashr[y];
	siz[x] = siz[y];
}

inline void pushup(int x)
{
	siz[x] = siz[ls(x)] + siz[rs(x)] + 1;
	Hashl[x] = Hashl[rs(x)] + p_[siz[rs(x)]] * Abs(val[x]) + p_[(siz[rs(x)] + 1)] * Hashl[ls(x)]; // 左边比较高的哈希 
	Hashr[x] = Hashr[ls(x)] + p_[siz[ls(x)]] * Abs(val[x]) + p_[(siz[ls(x)] + 1)] * Hashr[rs(x)]; // 右边比较高的哈希 
}
//int flag = 0;
//inline void Debug(){int op;return;}
inline void split(int now,int k,int &x,int &y)
{
//	if(flag == 2333) Debug();
	if(!now) return (void)(x = y = 0);
	if(siz[ls(now)] + 1 <= k)
	{
		x = now;
		split(rs(now),k - siz[ls(now)] - 1,rs(x),y);
	}
	else
	{
		y = now;
		split(ls(now),k,x,ls(y));
	}
	pushup(now);
}

inline int Merge(int x,int y)
{
	if(!x || !y) return x | y;
	if(pri[x] < pri[y])
	{
		rs(x) = Merge(rs(x),y);
		pushup(x);
		return x;
	}
	else
	{
		ls(y) = Merge(x,ls(y));
		pushup(y);
		return y;
	}
}

inline void split_(int now,int k,int &x,int &y)
{
//	if(flag == 23) Debug(); 
	if(!now) return (void)(x = y = 0);
	if(siz[ls(now)] + 1 <= k)
	{
		x = Rub();//++cnt;
		cop(x,now);
		split_(rs(x),k - siz[ls(now)] - 1,rs(x),y);
		pushup(x);
	}
	else
	{
		y = Rub();//++cnt;
		cop(y,now);
		split_(ls(y),k,x,ls(y));
		pushup(y);
	}
}

#define Merge_ Merge

inline ill lHash(int p,int l,int r)
{
	if(l > r) return 0;
	int x,y,z;
	ill Ans(0);
	split(p,r,x,y);
	split(x,l-1,x,z);
	Ans = Hashl[z];
	p = Merge(Merge(x,z),y);
	return Ans;
}

//int flag = 0;
inline ill rHash(int p,int l,int r)//合并分裂之后树的形态是不变的所以不用新建节点
{
//	if(flag && l == 2 && r == 3) Debug();//flag = 2333,cerr<<siz[x]<<'\n',
	if(l > r) return 0;
	int x,y,z;
	ill Ans(0);
	split(p,r,x,y);
//	flag = 1;
	split(x,l-1,x,z);
	Ans = Hashr[z];
	p = Merge(Merge(x,z),y);
	return Ans;
}

inline void FHQ_rec(int x)
{
	if(ls(x)) FHQ_rec(ls(x));
	use[x] = 1;
	if(rs(x)) FHQ_rec(rs(x));
}

struct trans{
	int id,l_,r_;
};

struct Node{//建一棵线段树,每个节点维护一棵平衡树,平衡树中维护所有字符串
//同时维护左向哈希和右向哈希 
//	int l,r;
	int rt;
	short typ;
	int sl,sr;
	Node *ls,*rs;
//	int ls,rs;
	
	inline void rec()
	{
		if(ls) ls->rec();
		FHQ_rec(rt);
		if(rs) rs->rec();
	 } 
//	#define LS(x) 
	inline void cl(){rt = typ = sl = sr = 0;}
	inline bool inrange(int l,int r,int L,int R){return L <= l && r <= R;}
	inline bool outofrange(int l,int r,int L,int R){return r < L || R < l;}
	inline int debug(){int op =1;return op;}
	inline void pushup() // pushup -> O()
	{
//		if(flag && l == 1 && r == 5) debug(),flag = 23;
		typ = ls->typ & rs->typ;
		if(!typ) return;
		int ql , qr , q;
		ql = ls->sr,qr = rs->sl;
		q = min(ql,qr);
		ill a1 = lHash(rs->rt,1,q);
		ill a2 = rHash(ls->rt,siz[ls->rt] - q + 1,siz[ls->rt]);
		if(a1 == a2)
		{
			int x,y,a,b;
			split_(ls->rt,siz[ls->rt] - q,x,y);
			split_(rs->rt,q,a,b);
			rt = Merge_(x,b);
//			printf("ls->sl = %d ls->sr = %d q = %d\n",ls->sl,ls->sr,q);
//			printf("rs->sl = %d rs->sr = %d q = %d\n",rs->sl,rs->sr,q);
			sl =ls->sl + qr - q;
			sr =rs->sr + ql - q;
//			printf("rt = %d sl = %d sr = %d l = %d r = %d typ = %d\n",rt,sl,sr,l,r,typ);
		}
		else typ = 0;
	}
	
	inline trans query(int l,int r,int L,int R)
	{
		if(inrange(l,r,L,R))
		{
			return typ?(trans){rt,sl,sr}:(trans){-2,0,0};
		}
		else
		{
			if(!outofrange(l,r,L,R))
			{
				int mid = l + r >> 1;
				trans op1 = ls->query(l,mid,L,R),op2 = rs->query(mid+1,r,L,R);
				int r1 = op1.id;
				int r2 = op2.id;
				int x,y,a,b;
				if(r1 == -2 || r2 == -2) return (trans){-2,0,0};
				if(r1 != -1 && r2 != -1) 
				{
					int ql,qr,q;
					ql = op1.r_;
					qr = op2.l_;
					q = min(ql,qr);
					ill a1,a2;
					a1 = lHash(r2,1,q);
					a2 = rHash(r1,siz[r1] - q + 1,siz[r1]);
					if(a1 == a2)
					{
						split_(r1,siz[r1] - q,x,y);
						split_(r2,q,a,b);
						return (trans){Merge_(x,b),op1.l_ + qr - q,op2.r_ + ql - q};
					}
					else return (trans){-2,0,0};
				}
				else if(r1 != -1) return op1;
				else if(r2 != -1) return op2;
				
			}
			else return (trans){-1,0,0};
		}
	}
	
	inline void upd(int l,int r,int pos,int vl)
	{
		if(inrange(l,r,pos,pos))
		{
			cl();
			rt = New(vl);
//			sl = sr = 0;
			if(vl > 0) typ = sr = 1;
			else typ = sl = 1;
		}
		else
		{
			if(!outofrange(l,r,pos,pos))
			{
				int mid = l + r >> 1;
				ls->upd(l,mid,pos,vl);
				rs->upd(mid+1,r,pos,vl);
				cl();
				pushup();
			}
			
		}
	}
	
}mem[np * 2],*pool = mem,*rot;
inline Node *New(){return ++pool;}
inline Node *build(int L,int R)
{
	Node *u = New();
//	u->l = L,u->r = R;
	if(L == R)
	{
		u->ls = u->rs = NULL;
		u->rt = New(pare[L]);
		if(pare[L] > 0) u->typ = u->sr = 1;
		else u->typ=u->sl = 1;
	}
	else
	{
		int mid = L + R >> 1;
		u->ls = build(L,mid);
		u->rs = build(mid + 1,R);
		u->pushup();
	}
	return u;
}

signed  main()
{
	int n,k,q;
	p_[0] = 1;
//	cout<<"*";
//	cerr<<n<<" ";
	for(int i=1;i<=1e5;i++) p_[i] = p_[i - 1] * base;//cerr<<"*";
	read(n);
	read(k);//cerr<<"*";
//	cerr<<n<<'\n';
	for(int i=1;i<=n;i++) read(pare[i]);//,cerr<<i<<" ";
//	cerr<<"*";//scanf("%d",&pare[i]);cerr<<"*";//read(pare[i]),cerr<<"*";
//	cout<<"*";
	rot = build(1,n);
	read(q);
	
//	q = 30746;
	for(int i=1,opt,pos,vl,l,r;i <= q;i++)
	{
//		if(i == 30742) flag = 1;
//		else flag = 0;
		read(opt);
		switch(opt)
		{
			case 1:{
				read(pos);
				read(vl);
//				if(flag) rot->upd_(pos,vl);
//				else 
				rot->upd(1,n,pos,vl);
//				pare[pos] = vl;
				break;
			}
			case 2:{
//				cout<<i<<" ";
				read(l);
				read(r);
				trans ans = rot->query(1,n,l,r);
				if(ans.id == -2) printf("No\n");
				else if(ans.l_ || ans.r_) printf("No\n");
				
				else printf("Yes\n");
				
				break;
			}
		}
		if(i % 10000 == 0) 
		{
			use.reset();
			rot->rec();
			tail = 1;
		}

//		printf("rt = %d sl = %d sr = %d typ = %d\n",rot->rt,rot->sl,rot->sr,rot->typ);
				
	}
	return 0;
 }

敲了 400+ 行,真是吐了(其实还是代码能力不行

posted @ 2021-09-26 21:58  ·Iris  阅读(173)  评论(0编辑  收藏  举报