SSLOJ 1195.健美猫


题目:

传送门


分析:

看到旋转操作,小编一开始想到的是最小表示法,然后开始愉快的码题,起初还算顺利,但很快就遇到了硬茬——绝对值!
我们先设 x x x为题目中的 s s s序列
∣ x 1 − 1 ∣ + ∣ x 2 − 2 ∣ + ∣ x 3 − 3 ∣ … … |x_1-1|+|x_2-2|+|x_3-3|…… x11+x22+x33
然后经过一次旋转操作后,就变成了
∣ x 1 − n ∣ + ∣ x 2 − 2 + 1 ∣ + ∣ x 3 − 3 + 1 ∣ … … |x_1-n|+|x_2-2+1|+|x_3-3+1|…… x1n+x22+1+x33+1
这样我们就可以看到,除去第一个,其他的绝对值中的数都加了 1 1 1
可偏偏就是这个绝对值,使得我们需要统计正、负数的个数
显然,这个用最小表示法是无法实现的,所以我就在最小表示法的思想上套用了权值树状数组
用树状数组来统计负数的个数


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring> 
#include<cstdlib>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<list>
#include<ctime>
#include<iomanip>
#include<string>
#include<bitset>
#include<deque>
#include<set>
#define LL long long
#define ch cheap
#define XJQ %%%
#define offset (n<<1)
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
int min(int x,int y) {return x<y?x:y;}
int now,king;
int x[2000005];int tree[4000010];
int main(){
	int n=read();
	int big=n*4+1;
	for(int i=1;i<=n;i++)
	  x[i]=read();
	LL king=0;
	for(int i=1;i<=n;i++)
	{
		king+=abs(x[i]-i);
		for(int j=x[i]-i+offset;j<=big;j+=(j&-j))
			tree[j]++;
	}
	LL now=king;
	for(int i=2;i<=n;i++)
	{
		for(int j=x[i-1]-(i-1)+offset;j<=big;j+=(j&-j))
			tree[j]--;
		int f=0;
		for(int j=offset-i+1;j>0;j-=(j&-j))
			f+=tree[j];
		now-=f;
		now+=(n-1-f);
		now=now-abs(x[i-1]-1)+abs(x[i-1]-n);
		king=min(king,now);
		for(int j=x[i-1]-n-(i-1)+offset;j<=big;j+=(j&-j))
			tree[j]++;
	}
	cout<<king;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值