【JZOJ6292】序列【思维】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/6292
在这里插入图片描述


思路:

这道题移动数字不是很好办,考虑移动下标,这样和移动数字是等价的。
如果第 i i i个数字的下标移动到了 i + 1 i+1 i+1,那么原来的 ∣ a i − i ∣ |a_i-i| aii就变成了 ∣ a i − i − 1 ∣ |a_i-i-1| aii1,那么原来 a i ≤ i a_i\leq i aii的贡献就增加了1, a i > i a_i>i ai>i的贡献就减少了1。
所以我们需要记录下每一个时刻有多少个 a i ≤ i a_i\leq i aii,同时又有多少个 a i > i a_i>i ai>i。考虑到在移动下标时 a i a_i ai i i i的大小关系可能是会变的,所以我们需要开一个桶来记录移动了 i i i个单位后,有多少的大小关系会变动。
这样就做到了每次 O ( 1 ) O(1) O(1)修改,时间复杂度 O ( n ) O(n) O(n)


代码:

#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=5000010;
int n,s1,s2,a[N],t[N];
ll ans,minn;

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch))
		d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read();
	for (register int i=1;i<=n;i++)
	{
		a[i]=read();
		if (a[i]>i)
		{
			s1++;
			t[a[i]-i]++;
			minn+=(ll)a[i]-i;
		}
		else
		{
			s2++;
			minn+=(ll)i-a[i];
		}
	}
	ans=minn;
	for (register int i=1;i<=n;i++)
	{
		int k=n-i+1;
		ans=ans+(ll)s2-s1;
		ans=ans-(ll)(n-a[k]+1)+(ll)(a[k]-1);
		if (a[k]<=1) s2++; 
		else
		{
			s1++;
			t[i+a[k]-1]++;
		}
		s2--;
		s1-=t[i]; s2+=t[i];
		minn=min(minn,ans);
	}
	printf("%lld",minn);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值