AtCoder Regular Contest 077 E - guruguru

https://arc077.contest.atcoder.jp/tasks/arc077_c

 

有m个点围成一个圈,按顺时针编号为1到m,一开始可以固定一个位置x,每次操作可以往顺时针方向走一步或直接走到x。现在给出n个位置a[1..n],初始时在a[1],第i次要从a[i]走到a[i+1],在x可以任意选择的情况下使总步数最小。 

 

对于从a走到b来说

若选择的x=a 或 a+1,那么不会使步数减少

若选择的x=a+2,会使步数减少1

若选择的x=a+3,会使步数减少2

……

问题就变成了 给区间[l,r]加首项为1,公差为1的等差数列

 

给位置l 加1,位置l+1加2,位置l+2加3……位置r加r-l+1

那么令cnt[l]加1,cnt[r+1]减r-l+1+1,cnt[r+2]减r-l+1

对cnt做一遍前缀和,得到差分数组

在做一遍前缀和,可以得到本身的值

据说这个叫二阶差分

 

两遍前缀和后的cnt数组就是把位置选在x,会使原本的步数减少多少

取最大的一个,总步数减它就是答案

 

#include<cstdio>
#include<iostream>

using namespace std;

#define N 100001 

int a[N];
long long cnt[N<<1];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

int main()
{
    int n,m;
    read(n); read(m);
    for(int i=1;i<=n;++i) read(a[i]);
    int l,r;
    long long tot=0;
    for(int i=1;i<n;++i)
    {
        l=a[i];
        r=a[i+1];
        if(l>r) r+=m;
        tot+=r-l;
        if(r-l>1)
        {
            cnt[l+2]++; 
            cnt[r+1]-=r-(l+2)+2;
            cnt[r+2]+=r-(l+2)+1;
        }
    }
    for(int i=1;i<=m*2;++i) cnt[i]+=cnt[i-1];
    for(int i=1;i<=m*2;++i) cnt[i]+=cnt[i-1];
    long long ans=tot;
    for(int i=1;i<=m;++i) ans=min(ans,tot-cnt[i]-cnt[i+m]);
    cout<<ans;
}

 

posted @ 2018-03-20 21:12  TRTTG  阅读(254)  评论(0编辑  收藏  举报