SSLOJ 1195.健美猫

版权声明:喜欢请点个大拇指,感谢各位dalao。弱弱说下,转载要出处呦 https://blog.csdn.net/qq_35786326/article/details/83153022


题目:

传送门


分析:

看到旋转操作,小编一开始想到的是最小表示法,然后开始愉快的码题,起初还算顺利,但很快就遇到了硬茬——绝对值!
我们先设 x x 为题目中的 s s 序列
x 1 1 + x 2 2 + x 3 3 |x_1-1|+|x_2-2|+|x_3-3|……
然后经过一次旋转操作后,就变成了
x 1 n + x 2 2 + 1 + x 3 3 + 1 |x_1-n|+|x_2-2+1|+|x_3-3+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;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/83153022