题目描述:
这个游戏是这样的,你有一个初始序列S ,你每次可以选择一段任意长度的连续区间,把他们+1 再膜k,给定目标序列,你需要尝试用尽量少的操作次数将初始序列变为目标序列。作为一名优秀的OIer,您认为这个游戏十分naive,所以您打算撸一个游戏脚本来取到最优解。
输入:
第一行一个T 表示数据组数。
对于每组数据,第一行两个整数表示序列长度和模数。
接下来两行分别包含n 个整数,表示初始序列和目标序列。
输出:
对于每组数据,输出一行一个整数表示最少操作次数。
数据范围:
1<=T<=5
对于10% 的数据满足n≤1
对于30% 的数据满足n≤10
对于50% 的数据满足n≤100
对于70% 的数据满足n≤5000
对于100% 的数据满足1≤n≤100000,1≤k≤100,0≤x1≤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; }