[Ynoi2015]即便看不到未来

题目大意:

给定一个序列,每次询问,给出一个区间$[l,r]$。

设将区间内的元素去重后重排的数组为$p$,求$p$中长度为$1\sim 10$的极长值域连续段个数。

长度为$L$的极长值域连续段的定义为:存在$l,r\in[1,|p|]$满足$r-l+1=L$且$\forall i\in[l,r),p_i=p_{i+1}-1$,且$l-1,r$和$l,r+1$均不满足条件。

解题思路:

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩

珂朵莉……


 

看memset0的题解都要想那么久窝真是没用

这题考虑对询问离线,然后从左往右依次加上数,计算贡献。

考虑一个数$x$新加进来,会和哪些数产生贡献。

1. 在$x$上一次出现之后的数。

2. 值域在$[x-11,x+11]$之间的数(这里取$11$是因为还需要减去贡献),当然只考虑和当前位置最接近的那个数。

我们记录每个数之前出现的位置,然后把可能和当前$x$产生贡献的数拿出来,重新计算贡献。

把这些数按照位置从大到小排序,按次序加入。每加入一个数,扫一遍$x+1$开头的和$x-1$结尾的极长值域连续段,减去这一部分贡献。然后加上两边合起来的贡献。每次的贡献影响到的是加进来的数的位置到下一个加进来的数的位置之前的所有位置。

然后就搞个区间修改、单点查询的数据结构,用树状数组即可。

时间复杂度$O(22^2n+22n\log 22)$。

有点卡常数。

C++ Code:

#include<algorithm>
#include<vector>
#include<cstdio>
#include<cctype>
using namespace std;
typedef long long LL;
const unsigned U1=-1;
namespace fastread{
	char C[(int)1e8],*buf=C;
	inline void init(){C[fread(C,1,(int)1e8-1,stdin)]='\n';fclose(stdin);}
	inline int readint(){
		int d=0;
		while(!isdigit(*buf))++buf;
		while(isdigit(*buf))d=d*10+(*buf++^'0');
		return d;
	}
}
char FW[(int)2e7],*buf=FW;
const int N=1e6+5;
int n,m,a[N],pre[N],ans[N][11];
bool vis[N];
vector<LL>num,vec[N];
int b[11][N];
inline void add(int i,int x,const int&p){for(;i<=n;i+=i&-i)b[p][i]+=x;}
inline int ask(int i,const int&p){int x=0;for(;i;i^=i&-i)x+=b[p][i];return x;}
inline void modify(int l,int r,int x,const int&p){add(l,x,p),add(r+1,-x,p);}
int main(){
	fastread::init();
	n=fastread::readint(),m=fastread::readint();
	for(int i=1;i<=n;++i)a[i]=fastread::readint();
	for(int i=1;i<=m;++i){
		int l=fastread::readint(),r=fastread::readint();
		vec[r].push_back((LL)l<<32|i);
	}
	for(int it=1;it<=n;++it){
		const int v=a[it],mn=max(1,v-11),mx=min(1000000,v+11);
		num.clear();
		for(int i=mn;i<=mx;++i)
			if(pre[i]&&pre[i]>pre[v])num.push_back((LL)pre[i]<<32|i);
		num.push_back((LL)it<<32|v),num.push_back((LL)pre[v]<<32|1000002);
		sort(num.rbegin(),num.rend());
		for(int i=0;i<num.size()-1;++i){
			int l=0,r=0;
			vis[num[i]&U1]=1;
			while(v-l-1&&l<=10&&vis[v-l-1])++l;
			while(v+r+1<=1000000&&r<=10&&vis[v+r+1])++r;
			const int all=l+r+1;
			if(l&&l<=10)modify((num[i+1]>>32)+1,num[i]>>32,-1,l);
			if(r&&r<=10)modify((num[i+1]>>32)+1,num[i]>>32,-1,r);
			if(all<=10)modify((num[i+1]>>32)+1,num[i]>>32,1,all);
		}
		pre[v]=it;
		for(int i:num)vis[i]=0;
		for(LL i:vec[it])
			for(int j=1;j<=10;++j)
				ans[i&U1][j]=ask(i>>32,j)%10;
	}
	for(int i=1;i<=m;++i,*buf++='\n')
		for(int j=1;j<=10;++j)
			*buf++=ans[i][j]^'0';
	fwrite(FW,1,buf-FW,stdout);
	return 0;
}

  

在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩
珂朵莉……
---
~~看memset0的题解都要想那么久窝真是没用~~
这题考虑对询问离线,然后从左往右依次加上数,计算贡献。
考虑一个数$x$新加进来,会和哪些数产生贡献。
1. 在$x$上一次出现之后的数。2. 值域在$[x-11,x+11]$之间的数(这里取$11$是因为还需要减去贡献),当然只考虑和当前位置最接近的那个数。
我们记录每个数之前出现的位置,然后把可能和当前$x$产生贡献的数拿出来,重新计算贡献。
把这些数按照位置从大到小排序,按次序加入。每加入一个数,扫一遍$x+1$开头的和$x-1$结尾的极长值域连续段,减去这一部分贡献。然后加上两边合起来的贡献。每次的贡献影响到的是加进来的数的位置到下一个加进来的数的位置之前的所有位置。
然后就搞个区间修改、单点查询的数据结构,用树状数组即可。
时间复杂度$O(22^2n+22n\log 22)$。
有点卡常数。

转载于:https://www.cnblogs.com/Mrsrz/p/10617094.html

题目 P5413 "YNOI2019 骑单车" 是一个经典的动态规划和贪心算法的问题。该题主要涉及两个概念:路径规划和状态转移方程。 **背景描述** 假设你在一个二维网格上,每个单元格代表一个地点,你需要从起点出发骑车到终点,并尽可能地减少骑行时间。网格中的每个单元格都有两种可能的状态:平地(速度不变)或斜坡(速度减半)。你的目标是找到一条最短的路线。 **关键点解析** 1. **动态规划**:通常用于求解最优化问题。在这个问题中,我们可以定义一个二维数组 dp[i][j] 表示从起点到位置 (i, j) 的最短行驶时间。状态转移方程会根据当前位置的性质(平地还是斜坡)以及到达此位置的最短路径来自之前的节点计算。 2. **状态转移**:对于平地,dp[i][j] = dp[pi][pj] + cost,表示直接移动到相邻位置的时间;对于斜坡,dp[i][j] = min(dp[pi][pj], dp[pi][pj-1]) + cost/2,因为斜坡速度减半,所以需要选择更早的时刻经过。 3. **贪心策略**:有时候,为了达到全局最优,初始看起来不是最优的选择可能是正确的。但在这个问题中,贪心策略可能并不适用,因为我们不能仅依据当前状态做出决策,需要考虑到整个路径。 4. **边界条件**:初始化 dp 数组时,起点时间设为 0,其余位置设为正无穷大,保证一开始就只会向可达的位置移动。 **代码实现** 实现这样的动态规划算法通常需要用到一个优先队列(如最小堆),以便于高效地查找之前节点的最优时间。 **相关问题--:** 1. 如何设计状态转移方程来处理平地和斜坡的情况? 2. 这个问题是否存在剪枝操作以提高效率? 3. 如果网格大小非常大,如何避免存储所有 dp 值导致的空间爆炸?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值