[JZOJ5343]健美猫<模拟>

【思路】

这个是一个非常容易看出来的模拟,但是模拟也是有技巧的

一般人的模拟思路一般就是移动元素或者下标

然后我就看到了一个有趣的思路

建立坐标轴

以i坐标为横坐标,以si为纵坐标,然后画一条斜率为1的直线,当y=x时,每个点到这条直线的竖直距离就是最开始没有旋转操作的值

如上图,我自己手写一组数据,然后画了一个图,绿色的细线的总长就是没有进行操作时的答案 

然后我们就来对这组数据进行操作,我们可以将这条直线左右平移,向左平移的时候,第1到第n-1个点的计算方式还是到直线的竖直距离,不过第n个点的计算方式不一样了

而向右平移,就是2到n点计算方式不变,第一个有变

刚刚那句话的正确性可以根据这张图来证明

然后我的做法是将直线左移,每次下标为n的点要特殊处理,其他点就是在移之前是在直线上方的是减去一个价值,而刚刚在直线上和直线下方的是加上一个价值,

然后只需要处理移动后点和直线的位置关系就行

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<queue>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<cstdlib>
 8 #define LL long long
 9 #define maxn 10005
10 using namespace std;
11 
12 int tmp,ans,n,up,down;
13 int num[maxn],a[maxn],cnt[maxn];
14 
15 int read(){
16     int x=0,f=1;char ch=getchar();
17     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
18     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
19     return x*f;
20 }
21 int main(){
22     n=read();
23     for(int i=1;i<=n;i++){
24         a[i]=read();
25         if(a[i]>i)tmp+=a[i]-i,cnt[a[i]-i]++,up++;
26         else tmp+=i-a[i],down++;
27     }ans=tmp;
28     for(int i=1;i<=n;i++){
29         tmp+=down-up;
30         tmp-=n+1-a[n-i+1];
31         tmp+=a[n-i+1]-1;
32         down--;
33         if(a[n-i+1]>1)up++,cnt[i+a[n-i+1]-1]++;
34         else down++;
35         up-=cnt[i];down+=cnt[i];
36         ans=min(ans,tmp);
37     }    
38     cout<<ans;
39 }
健美猫
原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7739585.html