DTOJ 3999: 游戏

Posted ssoiroor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DTOJ 3999: 游戏相关的知识,希望对你有一定的参考价值。

题目描述
这个游戏是这样的,你有一个初始序列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
",ans);
    }
    return 0;
}

 

























以上是关于DTOJ 3999: 游戏的主要内容,如果未能解决你的问题,请参考以下文章

DTOJ #3194. 去月球

bzoj-4009&&dtoj#2284. 接水果(fruit)

DTOJ2702:余数

DTOJ2704:数字互换

DTOJ2700:hello world

从片段调用 Google Play 游戏服务