[jzoj 5343] [NOIP2017模拟9.3A组] 健美猫 解题报告 (差分)

题目链接:

http://172.16.0.132/senior/#main/show/5343

题目:

题解:

记旋转i次之后的答案为$ans_i$,分别考虑每个元素对ans数组的贡献

若$s_i<i$:

对$ans_0,..,ans_{i-s_i}$,贡献分别是$i-s_i,i-s_i-1,...,0$

对$ans_{i-s_i+1},...,ans_{i-1}$,贡献分别是$1,...,s_i-1$

对$ans_i,...,ans_{n-1}$,贡献分别是$n-s_i,...,i+1-s_i$

若$s_i=i$:

对$ans_0$,贡献是$0$

对$ans_1,...,ans_{i-1}$,贡献分别是$1,...,i-1$

对$ans_i,...,ans_{n-1}$,贡献分别是$n-i,...,1$

若$s_i>i$:

对$ans_0,...,ans_{i-1}$,贡献分别是$s_i-i,...,s_i-1$

对$ans_i,...,ans_{i+n-s_i}$,贡献分别是$n-s_i,...,0$

对$ans_{i+n-s_i+1},...,ans_{n-1}$,贡献分别是$1,...,s_i-i-1$

发现都是公差为$1$或$-1$的等差数列,显然线段树可以维护,但是数据范围不允许而且我们也不需要。怎么办呢?我们差分

对ans数组二阶差分即可,注意二阶差分的同时需要在一阶差分消去影响

注意差分的题目手动模拟是很有必要的,每个差分独立考虑

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

const int N=2e6+15;
const ll inf=1e15;
int n;
ll s[N],cha1[3][N],cha2[3][N],a[N],b[N],p[N];
inline int read(){
    char ch=getchar();int s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++) s[i]=read();
    for (int i=1;i<=n;i++)
    {
        if (s[i]<i)
        {
            if (0<=i-s[i]) cha2[2][i-s[i]-1]++;
            if (i-s[i]+1<=i-1) {cha1[2][i-s[i]+1]++;cha1[2][i]--;cha1[1][i]-=s[i]-1;}
            if (i<=n-1) {cha2[2][n-1]++;cha2[2][i-1]--;cha2[1][n-1]+=i-s[i];cha2[1][i-1]-=n-s[i];}
        }
        if (s[i]==i)
        {
            if (i-s[i]+1<=i-1) {cha1[2][1]++;cha1[2][i]--;cha1[1][i]-=i-1;}
            if (i<=n-1) {cha2[2][n-1]++;cha2[2][i-1]--;cha2[1][i-1]-=n-i;}
        }
        if (s[i]>i)
        {
            if (0<=i-1) {cha1[2][0]++;cha1[2][i]--;cha1[1][0]+=s[i]-i-1;cha1[1][i]-=s[i]-1;}
            if (i<=i+n-s[i]) {cha2[2][i+n-s[i]-1]++;cha2[2][i-1]--;cha2[1][i-1]-=n-s[i];}
            if (i+n-s[i]+1<=n-1) {cha1[2][i+n-s[i]+1]++;cha1[2][n]--;cha1[1][n]-=s[i]-i-1;}
        }
    }
    for (int i=0;i<n;i++) p[i]=p[i-1]+cha1[2][i];
    for (int i=0;i<n;i++) cha1[1][i]+=p[i];
    for (int i=n-1;i>=0;i--) p[i]=p[i+1]+cha2[2][i];
    for (int i=n-1;i>=0;i--) cha2[1][i]+=p[i];
    for (int i=0;i<n;i++) a[i]=a[i-1]+cha1[1][i];
    for (int i=n-1;i>=0;i--) b[i]=b[i+1]+cha2[1][i];
    ll mi=inf;
    for (int i=0;i<n;i++) mi=min(mi,a[i]+b[i]);
    printf("%lld\n",mi);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xxzh/p/9843319.html