题目链接: 2018 ICPC Pacific Northwest Regional Contest - I-Inversions

题意

给出一个长度为\(n\)的序列,其中的数字介于0-k之间,为0表示这个位置是空的。现在可以在这些空的位置上任意填入1-k之间的数字(可以重复)。问最多可以总共有多少对逆序对。(如果\(i<j,p_i>p_j\),则称\((i,j)\)是一对逆序对)

\(1\leq n\leq 2*10^5,\ 1\leq k\leq 100\)


思路

  • 第一步,先证明最优的填入的序列一定是非降序的。这里可以用反证法。假设\(a\lt b\),对于序列\(seq_1,a,seq_2,b,seq_3\),这里seq表示一段数字。如果我们交换a和b的位置,可以发现,a原本的贡献中:

    1. \(seq_1\)里比\(a\)大的,保留了下来。
    2. \(seq_2\)里比\(a\)小的,必定也比\(b\)小。
    3. \(seq_3\)里比\(a\)小的,保留了下来。

    同样地,在b原本的贡献中:

    1. \(seq_1\)里比\(b\)大的,保留了下来。
    2. \(seq_2\)里比\(b\)大的,必定也比\(a\)大。
    3. \(seq_3\)里比\(b\)大的,保留了下来。

    因此,用非降序的序列来填空,至少不会比这个序列的其他排列方式差,也就可以认为这是最优的了。

    这里稍微提一下,如果用相似的方法,无法证明非升序是最优的。

  • 第二步,明确算法过程需要什么数据。这里需要用到的有:

  1. 原序列的:

    • 对于所有的数字1-k,每个位置与前面可组成的逆序对数。
    • 已产生的所有逆序对数。
  2. 填入空位的数:
    • 可与原序列产生的逆序对数
    • 填入序列之间产生的逆序对数。(大概是一个等差数列求和再减去相同部分)
  • 然后是核心部分。观察到k很小,所以我们枚举从左到右,从大到小填入序列。具体方法如下:

    1. 枚举从\(k到1\)的每个数字\(val\)。然后枚举任意连续的空位填入一串val。这样直接做的复杂度是\(O(n^2)\),所以需要一些优化。
    2. 首先用前缀和、差分把计算连续串的复杂度压到\(O(n)\)建立,\(O(1)\)查询。
    3. 然后再额外维护一个dp数组,记录对于这个val的情况,这样在取到更优值的时候,这个首部以前的地方就可以保证后续也可用。这样首部随着枚举尾部向后迁移,更新dp数组的复杂度也从\(O(n^2)\)降到了\(O(n)\)。
    4. 最后再用这个额外的dp数组去更新答案的dp数组,直接覆盖。算法执行完,就可以得到填空与原序列产生的总贡献,和填空序列里不变的子序列减少的贡献。
    5. 最终答案就是原序列的贡献+填空与原序列产生的贡献+填空之间产生的贡献(等差数列求和再减去不变的子序列减少的贡献,代码中为方便计算,减去的部分在填空与原序列产生的贡献中提前计算了)。

    这里因为我自己也还没有理解透彻,只停留在AC的程度,也有题目比较复杂的原因,可能讲的不够清楚。具体需要看代码里的一些注释辅助理解。


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const int maxk=1e2+10;
const ll inf=1e18;
int n,k;
int seq[maxn];
int preGt[maxn][maxk];//i,j: 下标从1到i这么多个大于j的数
int cnt[maxk];//至今有多少个数大于i
ll dp[maxn];//在i之前填空的贡献,减去相等序列本应有的贡献
ll curdp[maxn];
ll presum[maxn];//前缀和
ll getsum(ll num){
    //1...n等差数列求和
    return num*(num+1)/2;
}
int main(){
    // freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    cin>>n>>k;
    int empty=0;
    ll ori=0;//原序列的逆序对数
    for(int i=1;i<=n;++i){
        cin>>seq[i];
        if(seq[i]==0){
            ++empty;
        }
        else{
            for(int j=0;j<seq[i];++j)
                ++cnt[j];
            ori+=cnt[seq[i]];
        }
        for(int j=0;j<=k;++j)
            preGt[i][j]=cnt[j];
    }
//////////////////////
    for(int i=1;i<=n;++i)
        dp[i]=-inf;
    for(int val=k;val>=1;--val){
        //先用大的数填空
        int tot=0;
        ll sum=0;//要注意的是,这里的sum是前缀和
        for(int i=1;i<=n;++i){
            if(seq[i]) continue;

            //加上i左边比val大的数
            sum+=preGt[i][val];
            //再加上i右边比val小的数,这里用总数-比val-1大的数量=比val小的数量
            //然后再总的减掉左边的,就是右边的。
            sum+=(preGt[n][0]-preGt[n][val-1])-(preGt[i][0]-preGt[i][val-1]);
            presum[++tot]=sum;
        }
        int emptyR=1;
        int emptyL=1;
        int curL=1;
        int curst=1;
        //枚举这串val填空的尾部
        for(int ed=1;ed<=n;++ed){
            ll mx=-inf;
            if(seq[ed]) continue;
            curL=emptyL;
            for(int st=curst;st<=ed;++st){
                if(seq[st]) continue;
                //从上一个状态加上,L到R之间填空val的贡献,减去这串本该下降的空位产生的贡献。
                //例如2,1贡献了一个逆序对,但2,2就不贡献了。然后对比已有状态看是否更优。
                ll tmp=dp[curL-1]+(presum[emptyR]-presum[curL-1])-getsum(emptyR-curL);
                if(tmp<mx) break;
                if(tmp>mx){
                    mx=tmp;
                    emptyL=curL;
                    curst=st;
                }
                ++curL;
            }
            curdp[emptyR]=max(dp[emptyR],mx);
            ++emptyR;
        }
        swap(dp,curdp);
    }
    cout<<ori+dp[empty]+getsum(empty-1)<<'\n';
    return 0;
}

2018 ICPC Pacific Northwest Regional Contest I-Inversions 题解的更多相关文章

  1. Contest Setting 2018 ICPC Pacific Northwest Regional Contest dp

    题目:https://vj.69fa.cn/12703be72f729288b4cced17e2501850?v=1552995458 dp这个题目网上说是dp+离散化这个题目要对这些数字先处理然后进 ...

  2. 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) 题解

    [题目链接] A - Alphabet 最长公共子序列.保留最长公共子序列,剩余的删除或者补足即可. #include <bits/stdc++.h> using namespace st ...

  3. 2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)

    2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) 思路: A Exam 思路:水题 代码: #include<bits ...

  4. 2018 ACM-ICPC Asia Beijing Regional Contest (部分题解)

    摘要 本文主要给出了2018 ACM-ICPC Asia Beijing Regional Contest的部分题解,意即熟悉区域赛题型,保持比赛感觉. Jin Yong’s Wukong Ranki ...

  5. The 2018 ACM-ICPC Asia Qingdao Regional Contest(部分题解)

    摘要: 本文是The 2018 ACM-ICPC Asia Qingdao Regional Contest(青岛现场赛)的部分解题报告,给出了出题率较高的几道题的题解,希望熟悉区域赛的题型,进而对其 ...

  6. 2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) Solution

    A:Exam Solved. 温暖的签. #include<bits/stdc++.h> using namespace std; ; int k; char str1[maxn], st ...

  7. 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) Solution

    A:Alphabet Solved. 签. #include<bits/stdc++.h> using namespace std; ]; ]; int main(){ scanf(); ...

  8. 2015-2016 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) S Surf

    SurfNow that you've come to Florida and taken up surng, you love it! Of course, you've realized that ...

  9. 2018-2019 ACM-ICPC Pacific Northwest Regional Contest C Contest Setting(DP)

    比赛链接:Contest Setting C题 题意:$n$道题目,每道题目难度为$ai$,选择$k$道难度不同的题目,有多少种选择方案.$1<=k<=n<=1000,1<=a ...

随机推荐

  1. Python多线程同步互斥锁

    接着上篇多线程继续讲,上篇最后的多线程共享全局变量对变量的处理值出错在本文中给出解决方案. 出现这个情况的原因是在python解释器中GIL全局解释器锁. GIL:全局解释器锁,每个线程在执行的过程都 ...

  2. Jenkins Pipeline 持续集成

    Jenkins Pipeline 持续集成 Pipeline Script 执行流程 在使用Pipeline之前请确保Jenkins是2.x版本以上,并且安装了Pipeline插件. Jenkins提 ...

  3. spring08

    这里主要学习的是关于spring之后中与ioc不同的aop技术:面向切面编程是spring基石之一: 解决代码混乱文体,代码分散,当部分修改时,要逐个修改当更多的日志以及验证介入之后会使代码变得更加的 ...

  4. python 天天生鲜项目

    python 天天生鲜项目 django版:https://github.com/Ivy-1996/fresh flask版:https://github.com/Ivy-1996/flask-fre ...

  5. ZooKeeper 如何保证数据一致性?

    在分布式场景中,ZooKeeper 的应用非常广泛,比如数据发布和订阅.命名服务.配置中心.注册中心.分布式锁等. 在分布式场景中,ZooKeeper 的应用非常广泛,比如数据发布和订阅.命名服务.配 ...

  6. 玩家的numpertpry 对象 中 不仅仅要同步 君主武将的等级,阶级也要同步

    因为好多列表 中 需要 批量查询 玩家的等级 和阶级(用来显示玩家icon颜色用的),如果阶级 在numperty 中已同步 的话,就不用批量去查玩家武将列表了.同理如果其他属性也经常用的话也可以同步 ...

  7. 文档根元素 "beans" 必须匹配 DOCTYPE 根 "null"

    文档根元素 "beans" 必须匹配 DOCTYPE 根 "null" (2011-11-20 21:26:41) 转载▼ 标签: 杂谈 分类: spring- ...

  8. IKAnalyzer修改支持lucene8.0

    源码已经上传GitHub. 如果直接使用jar:IKAnalyzer-5.0.2. 在这我只是在原有代码结构上做了微调,让代码支持lucene8.0.

  9. 吾八哥学k8s(二):golang服务部署到kubernetes

    本篇主要讲解如何将golang的服务部署到kubernetes集群里,附带相关的golang的demo和yml文件内容.纯新手入门方式,生产服务需要完整的CI/CD流程来支持. golang服务代码 ...

  10. 在学习java之余,js的使用精髓-闭包和原型链

    这里分享下廖雪峰官网写的js教程,内容写的比较实用,易懂,其中简介的原型链和闭包的知识,小伙伴们一起上呀,畅游在知识的海洋中: 地址:https://www.liaoxuefeng.com/wiki/ ...