标签:ati 有一个 就是 目标 size ora font 不难 nbsp
题目描述
这个游戏是这样的,你有一个初始序列S ,你每次可以选择一段任意长度的连续区间,把他们+1 再膜k,给定目标序列,你需要尝试用尽量少的操作次数将初始序列变为目标序列。作为一名优秀的OIer,您认为这个游戏十分naive,所以您打算撸一个游戏脚本来取到最优解。
输入
第一行一个T 表示数据组数。
对于每组数据,第一行两个整数表示序列长度和模数。
接下来两行分别包含n 个整数,表示初始序列和目标序列。
输出
对于每组数据,输出一行一个整数表示最少操作次数。
样例输入
1
6 4
1 1 3 2 0 2
2 0 2 3 2 0
样例输出
4
提示
样例解释
四次操作的一种方式为:(1,6)(2,3)(2,3)(5,6)
数据范围
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和都减1操作,求变成全0的最小操作数。
这就先差分,然后正负抵消着操作即可,最后可能会剩一些正的(或负的)在加上(就是正数的和和负数的绝对值之和取max)
我们就朝着上述模型转换。
我们先考虑每个位置最少需要减多少次,设$a[i]$表示每个位置最少需要操作多少次,设$dif[i]=a[i]-a[i-1]$(就是$a[i]$的差分数组),那么在不考虑模的情况下,$ans=Σmax(dif[i],0)$。这里不考虑负的是因为操作数一定是正数,所以这一定是个正数序列,那么一定有:正数之和+负数之和>0 那么 正数之和-负数绝对值之和>0 那么 正数之和>负数绝对值之和。恩,这很显然。然而这句废话我想了半天才想出来。
不难发现$dif[i]$范围在$(-k,k)$,考虑模数带来的影响是使得一段区间的$a[i]$都$+k$,反映在$dif[i]$上就是使得$dif[l]+k$和$dif[r]-k$,所以我们要考虑,如何选取$l$和$r$使得答案更优。
比较显然的是,当$(dif[l]<0$&&$dif[r+1]>0)$选取了$l$和$r$才会使答案更优。具体做法,开一个桶用来装:如果这个位置成为$l$对答案的影响,从左到右扫一边,每次用一个桶将可以成为$l$的位置装起来如果$dif[i]<0$直接就进桶,对于可以成为$r$的位置(就是$dif[i]>0$的位置)我们每次就扫一边桶,找一下可以更新的最大值,如果找到了,就更新答案和桶。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define N 100005 using namespace std; int T,n,k,a[N],num[N],dif[N],ans,t[105]; int main(){ scanf("%d",&T); while(T--){ ans=0; memset(t,0,sizeof t); scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++){ int b;scanf("%d",&b); if(b<a[i]) b+=k; a[i]=b-a[i]; } for(int i=1;i<=n;i++) dif[i]=a[i]-a[i-1]; for(int i=1;i<=n;i++) ans+=max(dif[i],0); for(int i=1;i<=n;i++) if(dif[i]<0) t[dif[i]+k]++; else{ int del=0,p=0; for(int j=1;j<=k;j++) if(t[j]&&del<dif[i]-j) del=dif[i]-(p=j); if(del){ans=ans-del;t[dif[i]]++;t[p]--;}//要再考虑这个i这个位置成为之后的位置的l } printf("%d\n",ans); } return 0; }
标签:ati 有一个 就是 目标 size ora font 不难 nbsp
原文地址:https://www.cnblogs.com/ssoiRoor/p/9874613.html