【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)

【BZOJ4942】[Noi2017]整数

题目描述去uoj

题解:如果只有加法,那么直接暴力即可。。。(因为1的数量最多nlogn个)

先考虑加法,比较显然的做法就是将A二进制分解成log位,然后依次更新这log位,如果最高位依然有进位,那么找到最高位后面的第一个0,将中间的所有1变成0,那个0变成1。这个显然要用到线段树,但是复杂度是nlog2n的,肯定过不去。

于是我在考场上yy了一下,这log位是连续的,我们每次都要花费log的时间去修改一个岂不是很浪费?我们可以先在线段树上找到这段区间,然后在线段树上dfs下去,这样,时间复杂度就变成O(logn+那段区间在线段树上的大小)。因为一颗正常的线段树的大小就是4*n的,而这里的那段区间的大小是log的,所以我猜测复杂度应该是log*常数的。但是不会证,考完试旁边的大佬都十分怀疑我的复杂度,搞得我也非常怀疑,但是。。但是AC了。

正解貌似是O(nlog2n/32)的线段树+压位?听说考场上这么写的全被卡常了,体会到了wys的险恶用心~

回来重码了一发,交到uoj上TLE了,交到BZ上AC了(因为是均摊复杂度嘛~)。

欢迎大佬告诉我这个算法的真正复杂度~

#include <cstdio>
#include <cstring>
#include <iostream>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int N=30000040;
int n,A,B,nxt,f,len;
int s[N+1<<2],p[50];
void pushdown(int l,int r,int x)
{
	if(!s[x])	s[lson]=s[rson]=0;
	if(s[x]==r-l+1)
	{
		int mid=l+r>>1;
		s[lson]=mid-l+1,s[rson]=r-mid;
	}
}
void pushup(int x)
{
	s[x]=s[lson]+s[rson];
}
void dfs(int l,int r,int x)
{
	if(l==r)
	{
		s[x]+=p[l-B];
		while(s[x]>1)	p[l-B+1]++,s[x]-=2;
		while(s[x]<0)	p[l-B+1]--,s[x]+=2;
		return ;
	}
	pushdown(l,r,x);
	int mid=l+r>>1;
	dfs(l,mid,lson),dfs(mid+1,r,rson);
	pushup(x);
}
void updata(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)
	{
		dfs(l,r,x);
		return ;
	}
	pushdown(l,r,x);
	int mid=l+r>>1;
	if(a<=mid)	updata(l,mid,lson,a,b);
	if(b>mid)	updata(mid+1,r,rson,a,b);
	pushup(x);
}
void modify(int l,int r,int x,int a,int b,int c)
{
	if(a>b)	return ;
	if(a<=l&&r<=b)
	{
		s[x]=(r-l+1)*c;
		return ;
	}
	pushdown(l,r,x);
	int mid=l+r>>1;
	if(a<=mid)	modify(l,mid,lson,a,b,c);
	if(b>mid)	modify(mid+1,r,rson,a,b,c);
	pushup(x);
}
void nxt0(int l,int r,int x,int a)
{
	if(r<a||nxt<=l||s[x]==r-l+1)	return ;
	if(l==r)
	{
		nxt=l;
		return ;
	}
	int mid=l+r>>1;
	pushdown(l,r,x);
	nxt0(l,mid,lson,a),nxt0(mid+1,r,rson,a);
}
void nxt1(int l,int r,int x,int a)
{
	if(r<a||nxt<=l||!s[x])	return ;
	if(l==r)
	{
		nxt=l;
		return ;
	}
	int mid=l+r>>1;
	pushdown(l,r,x);
	nxt1(l,mid,lson,a),nxt1(mid+1,r,rson,a);
}
int query(int l,int r,int x,int a)
{
	if(l==r)	return s[x];
	pushdown(l,r,x);
	int mid=l+r>>1;
	if(a<=mid)	return query(l,mid,lson,a);
	return query(mid+1,r,rson,a);
}
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),rd(),rd(),rd();
	int i;
	for(i=1;i<=n;i++)
	{
		if(rd()==1)
		{
			A=rd(),B=rd(),f=1,len=0;
			if(!A)	continue;
			if(A<0)	f=-1,A=-A;
			while(A)	p[len++]=(A&1)*f,A>>=1;
			p[len++]=0,p[len]=0,updata(0,N,1,B,B+len-1);
			if(p[len]>0)
			{
				nxt=1<<30,nxt0(0,N,1,B+len);
				modify(0,N,1,B+len,nxt-1,0),modify(0,N,1,nxt,nxt,1);
			}
			if(p[len]<0)
			{
				nxt=1<<30,nxt1(0,N,1,B+len);
				modify(0,N,1,B+len,nxt-1,1),modify(0,N,1,nxt,nxt,0);
			}
		}
		else	A=rd(),printf("%d\n",query(0,N,1,A));
	}
	return 0;
}
//10 3 1 2 1 100 0 1 2333 0 1 -233 0 2 5 2 7 2 15 1 5 15 2 15 1 -1 12 2 15
时间: 2024-10-14 22:18:02

【BZOJ4942】[Noi2017]整数 线段树+DFS(卡过)的相关文章

[BZOJ4942][NOI2017]整数(线段树+压位)

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化. 松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章. 首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次. 然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之. 于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\log^{2}n)$. 但是看到极其相近的数据范

HDU 5692 线段树+dfs序

Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1779    Accepted Submission(s): 427 Problem Description 百度科技园内有n 个零食机,零食机之间通过n−1 条路相互连通.每个零食机都有一个值v ,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充,零食机的价值v

Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB 总提交次数:196   AC次数:65   平均分:58.62 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2013中国国家集训队第二次作业 问题描述 给定一棵N个节点的树,每个点有一个权值,有M个询问(a,b,c)若a 为1,回答b到c路径上的最小权值,若a为2,回答b到c路径上的最大权值,若a为3,回答b到c路径上的所有权值的

Codeforces 384E 线段树+dfs序

题目链接:点击打开链接 题意: 给定n个点,m个询问的无向树(1为根) 下面n个数表示每个点的权值 下面n-1行给出树 操作1:x点权值+v, x的第 i & 1 的儿子-v, 第 !(i&1) 的儿子+v 操作2:询问x点权值 dfs把树转成序列 根据深度把点分成2组 分别用线段树维护.. 然后Y一下 #include<stdio.h> #include<string.h> #include<iostream> #include<algorith

线段树+dfs序(Apple Tree )(Assign the task )

线段树+dfs序 给定一棵n个节点的树,m次查询,每次查询需要求出某个节点深度为h的所有子节点. 作为预处理,首先将树的所有节点按深度保存起来,每个深度的所有节点用一个线性结构保存,每个深度的节点相对顺序要和前序遍历一致. 然后从树的根节点进行dfs,对于每个节点记录两个信息,一个是dfs进入该节点的时间戳in[id],另一个是dfs离开该节点的时间戳out[id]. 最后对于每次查询,求节点v在深度h的所有子节点,只需将深度为h并且dfs进入时间戳在in[v]和out[v]之间的所有节点都求出

noi2017 T1 整数 ——线段树

loj.ac上有  题目传送门 不过我还是把题目搬过来吧 整数(integer)[题目背景]在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科 学家 P 博士正用它进行各种研究.不幸的是,这天台风切断了电力系统,超级计算机 无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你......[题目描述] P 博士将他的计算任务抽象为对一个整数的操作. 具体来说,有一个整数 x ,一开始为 0. 接下来有 n 个操作,每个操作都是以下两种类型中的一种:

HDU5692(线段树+dfs序)

Snacks Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Description 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化.小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次.另外,小度熊会对某个零食机的零食有所偏爱,要求

【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒.实验在一个封闭的局域网内进行.局域网内有n台计算机,编号为1~n.一些计算机之间通过网线直接相连,形

hdu 5692 Snacks 线段树+dfs

Snacks Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1295    Accepted Submission(s): 302 Problem Description 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充,零食机的价值v会时常发