codeforces gym102059 Timsort 思维

https://codeforces.com/gym/102059/problem/L

题目大意:给定一个包含 n n n个元素的序列, q q q次询问,每次询问给一个值 d i s dis dis,然后把原序列分段:每次分段从当前下标开始(初始为 1 1 1),选择最长的且连续的单调非降序列或者单调下降序列,设其长度为 l e n len len,若 l e n > = d i s len>=dis len>=dis,则这一段的长度为 l e n len len,没有坏元素,否者这一段的长度为 d i s dis dis,坏元素的个数为 d i s − l e n dis-len dislen,重复这一步骤直到分段完成。

思路: O ( n ) O(n) O(n)内可以计算出数组 f [ i ] 、 c [ i ] f[i]、c[i] f[i]c[i] f [ i ] f[i] f[i]表示从 i i i开始的连续非降序列的长度, c [ i ] c[i] c[i]表示从 i i i开始的连续单调递减序列的长度,对于每一个询问,暴力计算即可。当然需要把计算过的答案记下来,这样复杂度最高也不过: n / 2 + n / 3 + n / 4 + … … + n / n = n l g n n/2+n/3+n/4+……+n/n=nlgn n/2+n/3+n/4++n/n=nlgn。注意边界条件。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
 
const int maxn=1e5+5;
 
struct node
{
	int v1,v2;
	node(int a=0,int b=0)
	{
		v1=a,v2=b;
	}
}ans[maxn];
int a[maxn];
int c1[maxn],c2[maxn],c[maxn];
int n,q;
 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	int pre=1;
	a[0]=a[1],a[n+1]=a[n]-1;
	for(int i=1;i<=n+1;i++)
	{
		if(a[i]>=a[i-1])
			continue;
		for(int j=pre;j<=i-1;j++)
			c1[j]=i-1-j;
		pre=i;
	}
	pre=n;
	for(int i=n;i>=0;i--)
	{
		if(a[i]>a[i+1])
			continue;
		for(int j=pre;j>=i+1;j--)
			c2[j]=pre-j;
		pre=i;
	}
	for(int i=1;i<=n;i++)
		c[i]=max(c1[i],c2[i]);
	scanf("%d",&q);
	int dis;
	int tmp1,tmp2;
	while(q--)
	{
		scanf("%d",&dis);
		if(ans[dis].v1!=0)
			printf("%d %d\n",ans[dis].v1,ans[dis].v2);
		else
		{
			int i=1,times=0;
			while(i<=n)
			{
				++ans[dis].v1;
				tmp1=i+c[i]+1;
				tmp2=i+dis;
				tmp2=min(tmp2,n+1);
				if(tmp1>=tmp2)
					i=tmp1;
				else
					ans[dis].v2+=(tmp2-i-c[i]-1),i=tmp2;
			}
			printf("%d %d\n",ans[dis].v1,ans[dis].v2);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值