[NOI2005] 维护数列

总体思路其实跟用线段树维护区间最大字段和差不多,不过唯一麻烦的地方在于要算上自己。

然后我们可以开一个队列来回收那些被delete的点,这样可以节省空间,特别需要注意的是release的时候,标记什么的一定记得清空。

本来insert我是直接一个个merge的,这样就会导致特别慢,因此我们可以借助笛卡尔树的思想来直接O(n)的建树,最后再merge,这样的话会快非常多。具体来说就是维护一个右链,然后每次加入新的点,如果它的优先级比当前右链底部的点(栈顶)小,那么就直接弹栈。那么最后top的右儿子就是新加的节点,而最后一个弹出的点就是当前点的左儿子。

最后记得将所有点依次退栈,然后每一次退栈,都要相应update,包括上面的。
另外说一点,之所以每次release都要清空标记是因为,用这种O(n)的建树方法是不会考虑到原来的标记的,我原来写的release并没有清除标记,而原来直接merge的时候是会先下传标记,所以不会有问题,这也解释了为什么我只是改变建树的方式会错,因为没有清空标记。

#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<queue>
#define fo(i,a,b) for (register int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (register int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int inf=1e9;
const int N=1e6+5;
int n,m,rt,cnt;
int p[N],ls[N],rs[N],v[N],s[N];
int lm[N],rm[N],sm[N],sum[N];
bool t[N];
int set[N],x,y,k,z,l,r,tot,c[N],u;
int LC,RC;
char ch[20];
queue<int> q;
bool vis[N];
int a[N];
int max(int a,int b){
	return a<b ? b:a;
}
void swap(int &a,int &b){
	u=a; a=b; b=u;
}
void R(int &x){
	int t=0,p=1; char ch;
	for (ch=getchar();!('0'<=ch && ch<='9');ch=getchar()) {
		if (ch=='-') p=-1;
	}
	
	for (;('0'<=ch && ch<='9');ch=getchar()) t=t*10+ch-'0';
	x=t*p;
}
inline int New(int x){
	cnt=q.front(); q.pop();
	
	p[cnt]=rand();
	v[cnt]=x;
	ls[cnt]=rs[cnt]=0;
	s[cnt]=1;
	
	sum[cnt]=lm[cnt]=rm[cnt]=sm[cnt]=x;
	return cnt;
}
void upd(int x){
	LC=ls[x];
	RC=rs[x];
	
	s[x]=s[LC]+s[RC]+1;
	
	sum[x]=sum[LC]+sum[RC]+v[x];
	
	if (LC) {
		lm[x]=max(lm[LC], sum[LC]+v[x]);
		lm[x]=max(lm[x], sum[LC]+v[x]+lm[RC]);
	}
	else{
		lm[x]=max(v[x],v[x]+lm[RC]);
	}
	
	if (RC) {
		rm[x]=max(rm[RC], v[x]+sum[RC]);
		rm[x]=max(rm[x], rm[LC]+v[x]+sum[RC]);
	}
	else{
		rm[x]=max(v[x],rm[LC]+v[x]);
	}
	
	sm[x]=v[x];
	if (LC) {
		sm[x]=max(sm[x],sm[LC]);
		sm[x]=max(sm[x],rm[LC]+v[x]);
	}
	if (RC) {
		sm[x]=max(sm[x],sm[RC]);
		sm[x]=max(sm[x],lm[RC]+v[x]);
	}
	if (LC && RC) {
		sm[x]=max(sm[x],rm[LC]+v[x]+lm[RC]);
	}
}
inline void rev(int x){
	t[x]^=1;
	swap(lm[x],rm[x]);
}
inline void cha(int x,int m){
	set[x]=m;
	sum[x]=s[x]*m;
	v[x]=m;
	if (m>0) {
		lm[x]=rm[x]=sm[x]=s[x]*m;
	}
	else{
		lm[x]=rm[x]=sm[x]=m;
	}
}
void push(int x){
	if (set[x]!=inf){

		cha(ls[x],set[x]);
		cha(rs[x],set[x]);
		
		set[x]=inf;
		t[x]=0;//
		return;
	}
	if (t[x]){
		swap(ls[x],rs[x]);
		
		rev(ls[x]);
		rev(rs[x]);
		
		t[x]=0;
	}
}
void split(int u,int x,int &l,int &r){
	if (!u) {
		l=r=0;
		return;
	}
	push(u);
	if (s[ls[u]]+1<=x) {
		l=u;
		split(rs[u], x-s[ls[u]]-1, rs[u], r);
	}
	else{
		r=u;
		split(ls[u], x, l, ls[u]);
	}
	upd(u);
}
inline int merge(int l,int r){
	if (!l || !r) return l+r;
	push(l); push(r);
	if (p[l]>p[r]) {
		rs[l]=merge(rs[l],r);
		upd(l);
		return l;
	}
	else{
		ls[r]=merge(l,ls[r]);
		upd(r);
		return r;
	}
}
void pf(int x){
	if (!x) return;
	push(x);
	pf(ls[x]);
	printf("%d ",v[x]);
	pf(rs[x]);
}
inline void W(int x){
	if (!x) {
		puts("0");
		return;
	}
	if (x<0) {
		putchar('-');
		x=-x;
	}
	
	tot=0;
	while (x){
		c[++tot]=x%10;
		x/=10;
	}
	fd(i,tot,1) {
		putchar(c[i]+'0');
	}
	putchar('\n');
	
}
void rel(int x){
	if (!x) return;
	rel(ls[x]);
	set[x]=inf;
	t[x]=0;
	
	q.push(x); 
	rel(rs[x]);
}
int ss[N];
int build(int num)
{
    int x = 0;
    int last = 0;
    int top = 0;
    for (int i = 1; i <= num; i ++ )
    {
    	scanf("%d",&k);
        x = New(k);
        last = 0;
        while(top && p[ss[top]] < p[x])
        {
            upd(ss[top]);
            last = ss[top];
            ss[top -- ] = 0;
        }
        if(top)
        {
            rs[ss[top]] = x;
        }
        ls[x] = last;
        ss[++top] = x;
    }
    while(top)
        upd(ss[top -- ]);
    return ss[1];
}
int main(){
	
	fo(i,0,N-1) set[i]=inf;
	fo(i,1,5e5+1) q.push(i);
	
	srand(time(NULL));
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	
	R(n); R(m);
	fo(i,1,n) {
		R(x);
		New(x);
		rt=merge(rt,cnt);
	}
	
	while (m--) {
		scanf("%s",ch);
		switch (ch[2]){
			case 'S':
				z=0;
				int temp;
				R(x); R(y);
				temp=x;
			
				z=build(y);
				
				split(rt,temp,l,r); 
				rt=merge(l,z);
				rt=merge(rt,r);
				
				break;
			case 'L':
				R(x); R(y);
				split(rt,x+y-1,l,r);
				split(l,x-1,l,z);
				
				rel(z);
				rt=merge(l,r);
				break; 
			case 'K':
				R(x); R(y); R(k);
				split(rt,x+y-1,l,r);
				split(l,x-1,l,z);
				
				cha(z,k);
				
				rt=merge(l,z);
				rt=merge(rt,r);
				
				break;
			case 'V':
				R(x); R(y);
				split(rt,x+y-1,l,r);
				split(l,x-1,l,z);
				
				rev(z);
				
				rt=merge(l,z);
				rt=merge(rt,r);
				break;
			case 'T':
				R(x); R(y);
				split(rt,x+y-1,l,r);
				split(l,x-1,l,z);
				W(sum[z]);
				
				rt=merge(l,z);
				rt=merge(rt,r);
				break;
			case 'X':
				W(sm[rt]);
				break;
		}
	}
	return 0;
}

完结撒花。

upd:发现我之前建树小于号写成大于号,但是不影响正确性,不过确实会慢些。

posted @ 2023-04-28 13:09  gan_coder  阅读(26)  评论(0)    收藏  举报