zoukankan      html  css  js  c++  java
  • 【ACwing 100】InDec序列——差分

    (题面来自AcWing)

    给定一个长度为 n 的数列 a1,a2,,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。

    求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。

    输入格式

    第一行输入正整数n

    接下来n行,每行输入一个整数,第i+1行的整数代表ai

    输出格式

    第一行输出最少操作次数。

    第二行输出最终能得到多少种结果。

    数据范围

    0<n105,
    0ai<2147483648(因为不开long long再次见到了祖宗)

       

      看到题面之后的第一想法是差分之后暴搜方案,每次从(1~n + 1)选不同的两个数+1/-1,目标状态是d_2~d_n全部为0。然而数据范围显然不能接受。考虑序列操作的本质:我们要将差分序列d中除第一项外全部变为0,显然最优的方案是在d_2~d_n中优先选择一个负数、一个正数,分别+1、-1;设p、q分别为d中正、负项之和的绝对值,那么如上的第一种操作会把p、q中较小的一方降为0,这个过程最多执行min(p, q)次。之后正/负项中有一方会有剩余,我们可以选择把d_1与剩余项一同操作(相当于把原序列的某个前缀全部+1/-1),也可以选择d_(n+1)与剩余项一同操作(相当于操作后缀),最终都可以实现d_2~d_n全部为0。这一步操作最多执行|p - q|次。那么总的操作次数ans1满足:

      ans1 = min(p, q) + |p - q| = max(p, q)

      第二问要求统计在满足最小步数的情况下,可能产生的序列种类。由于最后序列每一项都相同,而d[1] = a[1],我们只要考虑最后d[1]有多少种即可。显然,上述最短操作过程不分先后,并且第一种操作不会对d[1]产生影响。考虑执行第二种操作后d[1]可能的取值:d[1]最多被增加/减少|p - q|次,最少被操作0次,并且d[1]可以被操作[0, |p - q|]中的任意多次。因此d[1]的取值有|p - q| + 1种,即ans2 = |p - q| + 1。

    代码:

    1. #include <cstdio>  
    2. #include <iostream>  
    3. #include <cmath>  
    4. #define rep(i, a, b) for(int i = a; i <= b; ++i)  
    5. #define per(i, b, a) for(int i = b; i >= a; --i)  
    6. #define maxn 100010  
    7. using namespace std;  
    8. long long d[maxn], a[maxn];  
    9. int main() {  
    10.     int n;   
    11.     ios::sync_with_stdio(0);  
    12.     cin >> n;  
    13.     long long p = 0, q = 0;  
    14.     rep(i, 1, n) {  
    15.         cin >> a[i];  
    16.         d[i] = a[i] - a[i - 1];  
    17.         if (i - 1)  
    18.             d[i] >= 0 ? p += d[i] : q += -d[i];  
    19.     }  
    20.     cout << max(p, q) << endl << abs(p - q) + 1;  
    21.     return 0;  
    22. }  
  • 相关阅读:
    读后感四
    读后感五
    公文流转系统
    统计文件中单词的频率,给出前N的单词
    读入一个文件输出每个字母的频率
    小工到专家
    从小工到专家读后感
    动手动脑 类与对象
    海芋
    棕竹
  • 原文地址:https://www.cnblogs.com/TY02/p/11307996.html
Copyright © 2011-2022 走看看