2018.10.29-dtoj-3999-游戏(game)

题目描述:

这个游戏是这样的,你有一个初始序列S ,你每次可以选择一段任意长度的连续区间,把他们+1 再膜k,给定目标序列,你需要尝试用尽量少的操作次数将初始序列变为目标序列。作为一名优秀的OIer,您认为这个游戏十分naive,所以您打算撸一个游戏脚本来取到最优解。

输入:

第一行一个T 表示数据组数。

对于每组数据,第一行两个整数表示序列长度和模数。

接下来两行分别包含n 个整数,表示初始序列和目标序列。

输出:

对于每组数据,输出一行一个整数表示最少操作次数。

数据范围:

 1<=T<=5

对于10% 的数据满足n1

对于30% 的数据满足n10

对于50% 的数据满足n100

对于70% 的数据满足n5000

对于100% 的数据满足1n100000,1k100,0x1≤n≤100000,1≤k≤100,0≤x(序列中的任一数)<k

算法标签:差分&桶排

思路:

片段共同加1可以想到差分,倘若没有%k,我们可以直接差分之后每次取ans+=max(0,c[i])。加了%k之后,当我们某一段值+k时对于差分的数组的影响是头c[i]+k,尾的c[i]-k。于是我们可以用桶存c[i]+k的值然后每次取最小值不断贪心。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5;int a[N],b[N],c[N],maxn,t[N],ans,k,n;
il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;}
il int get(int x){return abs(c[x-1]-c[x])+abs(c[x+1]-c[x]);}
int main()
{
    int T=read();while(T--){
        n=read();ans=0;k=read();
        for(int i=1;i<=n;i++)a[i]=read();for(int i=1;i<=n;i++)b[i]=read();
        for(int i=0;i<=k;i++)t[i]=0;
        for(int i=1;i<=n;i++){
            if(b[i]>=a[i])c[i]=b[i]-a[i];
            else c[i]=b[i]+k-a[i];
        }
        for(int i=n;i;i--)c[i]=c[i]-c[i-1];
        for(int i=1;i<=n;i++){
            int tot=0;
            if(c[i]>0){
                for(int j=1;j<c[i];j++)if(t[j]){tot=j;break;}
                if(tot){
                    ans=ans+tot,t[tot]--;t[c[i]]++;
                }
                else ans=ans+c[i];
            }
            if(c[i]<0)t[c[i]+k]++;
        }
        printf("%d
",ans);
    }
  return 0;
}
View Code
原文地址:https://www.cnblogs.com/Jessie-/p/9871127.html