2018 ICPC Pacific Northwest Regional Contest I-Inversions 题解
题目链接: 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原本的贡献中:
- \(seq_1\)里比\(a\)大的,保留了下来。
- \(seq_2\)里比\(a\)小的,必定也比\(b\)小。
- \(seq_3\)里比\(a\)小的,保留了下来。
同样地,在b原本的贡献中:
- \(seq_1\)里比\(b\)大的,保留了下来。
- \(seq_2\)里比\(b\)大的,必定也比\(a\)大。
- \(seq_3\)里比\(b\)大的,保留了下来。
因此,用非降序的序列来填空,至少不会比这个序列的其他排列方式差,也就可以认为这是最优的了。
这里稍微提一下,如果用相似的方法,无法证明非升序是最优的。
第二步,明确算法过程需要什么数据。这里需要用到的有:
- 原序列的:
- 对于所有的数字1-k,每个位置与前面可组成的逆序对数。
- 已产生的所有逆序对数。
- 填入空位的数:
- 可与原序列产生的逆序对数
- 填入序列之间产生的逆序对数。(大概是一个等差数列求和再减去相同部分)
然后是核心部分。观察到k很小,所以我们枚举从左到右,从大到小填入序列。具体方法如下:
- 枚举从\(k到1\)的每个数字\(val\)。然后枚举任意连续的空位填入一串val。这样直接做的复杂度是\(O(n^2)\),所以需要一些优化。
- 首先用前缀和、差分把计算连续串的复杂度压到\(O(n)\)建立,\(O(1)\)查询。
- 然后再额外维护一个dp数组,记录对于这个val的情况,这样在取到更优值的时候,这个首部以前的地方就可以保证后续也可用。这样首部随着枚举尾部向后迁移,更新dp数组的复杂度也从\(O(n^2)\)降到了\(O(n)\)。
- 最后再用这个额外的dp数组去更新答案的dp数组,直接覆盖。算法执行完,就可以得到填空与原序列产生的总贡献,和填空序列里不变的子序列减少的贡献。
- 最终答案就是原序列的贡献+填空与原序列产生的贡献+填空之间产生的贡献(等差数列求和再减去不变的子序列减少的贡献,代码中为方便计算,减去的部分在填空与原序列产生的贡献中提前计算了)。
这里因为我自己也还没有理解透彻,只停留在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 题解的更多相关文章
- Contest Setting 2018 ICPC Pacific Northwest Regional Contest dp
题目:https://vj.69fa.cn/12703be72f729288b4cced17e2501850?v=1552995458 dp这个题目网上说是dp+离散化这个题目要对这些数字先处理然后进 ...
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2) 题解
[题目链接] A - Alphabet 最长公共子序列.保留最长公共子序列,剩余的删除或者补足即可. #include <bits/stdc++.h> using namespace st ...
- 2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)
2018-2019 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) 思路: A Exam 思路:水题 代码: #include<bits ...
- 2018 ACM-ICPC Asia Beijing Regional Contest (部分题解)
摘要 本文主要给出了2018 ACM-ICPC Asia Beijing Regional Contest的部分题解,意即熟悉区域赛题型,保持比赛感觉. Jin Yong’s Wukong Ranki ...
- The 2018 ACM-ICPC Asia Qingdao Regional Contest(部分题解)
摘要: 本文是The 2018 ACM-ICPC Asia Qingdao Regional Contest(青岛现场赛)的部分解题报告,给出了出题率较高的几道题的题解,希望熟悉区域赛的题型,进而对其 ...
- 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 ...
- 2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) Solution
A:Alphabet Solved. 签. #include<bits/stdc++.h> using namespace std; ]; ]; int main(){ scanf(); ...
- 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 ...
- 2018-2019 ACM-ICPC Pacific Northwest Regional Contest C Contest Setting(DP)
比赛链接:Contest Setting C题 题意:$n$道题目,每道题目难度为$ai$,选择$k$道难度不同的题目,有多少种选择方案.$1<=k<=n<=1000,1<=a ...
随机推荐
- Fiddler插件---将Mapi请求自动转为HTTPRunner测试用例(YAML格式)
背景 继之前鼓捣出了Mapi解密插件之后,在团队内已经使用了三年之久,一跃成为团队最爱欢迎的测试工具之一(加个之一,低调谦虚一点). 随着团队推行HttpRunner搞接口自动化:编写和维护Case带 ...
- Java Interger小知识
Integer装箱与拆箱 装箱: Integer i = 10; 相当于:Integer i = Integer.valueOf(10); 拆箱: Integer i = 10; //装箱 int t ...
- CSS躬行记(4)——浮动形状
CSS规范新增了一个模块:CSS Shapes,shape-outside属性属于该模块,它能影响浮动元素周边内容流的形状(即浮动形状),可接收三类值:形状盒子.函数和图像. 一.形状盒子 形状盒子( ...
- Vulnhub DC-1靶机渗透
简介 DC-1靶机是为了入门渗透测试的人准备的入门级的靶机,该靶机共有5个flag,其中最后一个finalflag是真的flag,其他都是提示性flag.这个靶机虽然简单,但是还是能学到一些基本的思路 ...
- Python常见数据结构-Dictionary字典
字典基本特点 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中. 键是唯一的,如果重复最后的一个键值对会替换前面的,值不需 ...
- 37.4 net--TcpDemo2模拟用户登陆
package day35_net_网络编程.tcp传输.模拟用户登录; import java.io.*; import java.net.InetAddress; import java.net. ...
- python selenium使用
安装selenium #Python pip install selenium #Anaconda3 conda install selenium 下载浏览器版本对应的驱动文件 chrome chro ...
- Python操作rabbitmq系列(四):根据类型订阅消息
在上一章中,所有的接收端获取的所有的消息.这一章,我们将讨论,一些消息,仍然发送给所有接收端.其中,某个接收端,只对其中某些消息感兴趣,它只想接收这一部分消息.如下图:C1,只对error感兴趣,C2 ...
- Java方法的重点
方法就是完成功能一个语句集合体 使用方法的原则:方法的原子性,一个方法只实现一个功能. 方法的重载 1.函数名必须相同 2.形参列表必须不同(可以是个数不同,类型不同,不然完全一样) 3.返回值可以相 ...
- Xcode 6.3.1Mac版 V6.4.Beta3免费下载
Xcode for mac是Mac OS系统以及IOS系统开发者专用于构建 Mac OS X 及 iOS 应用程序的完整工具集 - Xcode 5 的工具经过重新设计,它们的性能更优秀.使用更容易,能 ...