首页 > 其他分享 >[NOI2005] 维护数列

[NOI2005] 维护数列

时间:2023-04-28 13:11:25浏览次数:44  
标签:rt return 数列 int merge NOI2005 split 维护 ls

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

然后我们可以开一个队列来回收那些被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;
}

完结撒花。

标签:rt,return,数列,int,merge,NOI2005,split,维护,ls
From: https://www.cnblogs.com/ganking/p/17361825.html

相关文章

  • 软件维护(Software maintenance)的流程
    软件维护(Softwaremaintenance)是一个软件工程名词,是指在软件产品发布后,因修正错误、提升性能或其他属性而进行的软件修改。软件维护主要根据需求变化或硬件环境的变化对应用程序进行部分或全部的修改,修改时应充分利用源程序。修改后要填写《程序修改登记表》,并在《程序变更通知书......
  • #yyds干货盘点# LeetCode程序员面试金典:外观数列
    题目:给定一个正整数n,输出外观数列的第n项。「外观数列」是一个整数序列,从数字1开始,序列中的每一项都是对前一项的描述。你可以将其视作是由递归公式定义的数字字符串序列:countAndSay(1)="1"countAndSay(n)是对countAndSay(n-1)的描述,然后转换成另一个数字字符串。前五项......
  • Azure DevOps(二)Azure Pipeline 集成 SonarQube 维护代码质量和安全性
    一,引言对于今天所分析的SonarQube,首先我们得了解什么是SonarQube?SonarQube又能帮我们做什么?我们是否在项目开发的过程中遇到人为Review代码审核规范?带着以上问题,开始今天的分析内容吧!!!1)什么是SonarQube?SonarQube是一种自动代码审查工具,用于检测代码中的错误......
  • 剑指 Offer 10- I. 斐波那契数列
     分析:偷个懒,上次做的一样的题代码:1classSolution(object):2deffib(self,n):3"""4:typen:int5:rtype:int6"""7ifn<2:8returnn9f=[0foriinra......
  • 兔子数列
    有一对兔子,从出生后的第三个月起,每个月生一对小兔子,假设所有的兔子都不死亡,30个月后会有多少兔子?分析:此问题是数学中著名的兔子数列问题(斐波那契数列),1,1,2,3,5.........其通式为:n=n-1+n-2;由此可以写出代码。#include<stdio.h>intmain(){ inti,f,f1=1,f2=1; printf("%d,%d",f1,......
  • Ubuntu维护整合
    任务要求:在服务器计算机使用ssh方式登录Ubuntu系统,根据模块A“局域网各设备IP配置”设置Ubuntu系统IP地址。在终端使用命令查询ssh服务运行情况。在终端使用命令查询哪些端口被使用。完成以上任务后请做以下步骤:(1)请将使用ssh命令成功登录Ubuntu系统的界面......
  • 斐波那契数列
    斐波那契数列 公式:F(n)=F(n-1)+F(n-2)  步骤: 1、初始化:第0项为0,第1项为1if(n<=1){  returnn;} 2、设置参数,确保第二项也为1intres=0;inta=0;intb=1; 3、从2开始循环到n,把自己的值赋给下一项for(inti=2;i<=n;i++){  res=a+......
  • 网络维护checklist
         ......
  • 03 | 写一个能产生斐波那契数列的range——惰性求值
    1.首先为了满足range概念的要求我们需要提供begin()和end()2.begin()和end()返回的应该是迭代器,注意这个地方两种可以返回两种不同类型(c++17后即可)3.为了满足迭代器概念的要求我们提供5个typedef,并根据std::input_iterator_tag类型决定我们要实现的“解引用函数”,......
  • 斐波拉契数列
    古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 先写出来前几个月的兔子数,分别是1、1、2、3、5、8、13、21、34......就是这样一组数列,第三个数是前两个数的和,也就是n=(n-1)+(n-2) ......