当前位置: 首页 > news >正文

解题报告-洛谷P3157 [CQOI2011] 动态逆序对

P3157 [CQOI2011] 动态逆序对

题目描述

对于序列 \(a\),它的逆序对数定义为集合

\[\{(i,j)| i<j \wedge a_i > a_j \} \]

中的元素个数。

现在给出 \(1\sim n\) 的一个排列,按照某种顺序依次删除 \(m\) 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

输入格式

第一行包含两个整数 \(n\)\(m\),即初始元素的个数和删除的元素个数。
以下 \(n\) 行,每行包含一个 \(1 \sim n\) 之间的正整数,即初始排列。
接下来 \(m\) 行,每行一个正整数,依次为每次删除的元素。

输出格式

输出包含 \(m\) 行,依次为删除每个元素之前,逆序对的个数。

输入输出样例 #1

输入 #1

5 4
1
5
3
4
2
5
1
4
2

输出 #1

5
2
2
1

说明/提示

【数据范围】
对于 \(100\%\) 的数据,\(1\le n \le 10^5\)\(1\le m \le 50000\)

【样例解释】
删除每个元素之前的序列依次为:

\[1,5,3,4,2 \]

\[1,3,4,2 \]

\[3,4,2 \]

\[3,2 \]


解题报告

这题很适合用来训练 CDQ 分治和树套树。

树套树的没什么好讲的,树状数组维护主席树,训练代码能力来的。主要讲 CDQ 分治。

首先把删除的操作转为倒着插入的操作,这样每个数就有一个出现时间。

要写 CDQ 分治,要先明确偏序情况,很显然,这题是个三维偏序:出现时间、在数组中的位置、数值大小。对于数组中的每一个数,我们建立结构体 \(node(t,p,x)\),并将原数组的信息转存到 \(node\) 数组 \(Q\) 中。那么,如果 \(Q_i\) 可以对 \(Q_j\) 的产生价值,当且仅当同时满足以下条件:

\[Q_i.t<Q_j.t \\ Q_i.p<Q_j.p \\ Q_i.x>Q_j.x \]

这样就完成了题意的转化。

然后就是 CDQ 分治的标准操作:先对第一维排序,用归并的方法维护第二维,用树状数组维护第三维。

这里用两点需要注意:

\(1\).对每个计算 \(Q_i\) 价值时,既要统计第二维 \(p\) 比它小并且第三维 \(x\) 比它大的,又要统计第二维 \(p\) 比它大并且第三维 \(x\) 比它小的

\(2\).归并时,我们统计的是每个时间点的新产生的价值,所以最后应该进行一次前缀和。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=101100;#define ckmax(x,y) ( x=max(x,y) )
#define ckmin(x,y) ( x=min(x,y) )inline int read()
{int f=1,x=0; char ch=getchar();while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }while(isdigit(ch))  { x=x*10+ch-'0';    ch=getchar(); }return f*x;
}int n,m;
int A[N];// ----------------------------------------#define lowbit(x) ( x&(-x) )
int T[N];inline void update(int x)
{ for(;x<=n;x+=lowbit(x)) T[x]++; }inline void Clear(int x)
{ for(;x<=n;x+=lowbit(x)) T[x]=0; }inline int query(int x)
{int sum=0;for(;x>0;x-=lowbit(x))sum+=T[x];return sum;
}//--------------------------------------struct ask
{int t,p,x;// 出现时间,出现的位置,数值大小
}Q[N],tmp[N];
int ans[N];bool rule_p(ask u,ask v)
{ return u.p<v.p; }inline void debug()
{cout<<endl;for(int i=1;i<=n;i++)printf("%d %d %d\n",Q[i].t,Q[i].p,Q[i].x);
}void cdq(int l,int r)
{if(l==r) return ;int mid=l+r>>1;cdq(l,mid),cdq(mid+1,r);int i=l,j=mid+1,pos=l;for(;j<=r;j++){while(i<=mid && Q[i].p<Q[j].p){update(Q[i].x);tmp[pos++]=Q[i++];}ans[Q[j].t]+=query(n)-query(Q[j].x);tmp[pos++]=Q[j];}for(int k=l;k<i;k++) Clear(Q[k].x);while(i<=mid) tmp[pos++]=Q[i++];for(int k=l;k<=r;k++)Q[k]=tmp[k];// debug();for(int k=r;k>=l;k--){if(Q[k].t<=mid) update(Q[k].x);else ans[Q[k].t]+=query(Q[k].x);}for(int k=r;k>=l;k--)if(Q[k].t<=mid) Clear(Q[k].x);
}// ----------------------------------------
int D[N];bool rule_t(ask u,ask v)
{if(u.t!=v.t) return u.t<v.t;return u.p<v.p;
}inline void Prepare()
{int tim=n;for(int i=1;i<=n;i++) D[A[i]]=i;for(int i=1;i<=n;i++) Q[i]=(ask){ 0,i,A[i] };for(int i=1;i<=m;i++){int x=read();Q[D[x]].t=tim--;}for(int i=n;i>0;i--)  if(!Q[i].t) Q[i].t=tim--;sort(Q+1,Q+n+1,rule_t);// debug();
}signed main()
{n=read(),m=read();for(int i=1;i<=n;i++) A[i]=read();Prepare(); cdq(1,n);for(int i=1;i<=n;i++) ans[i]+=ans[i-1];for(int i=n;i>n-m;i--)  printf("%d\n",ans[i]);// debug();return 0;
}
http://www.wxhsa.cn/company.asp?id=2244

相关文章:

  • DP 杂题
  • Java的变量和常量
  • 推荐7本书《MLIR编译器原理与实践》、《ONNX人工智能技术与开发实践》、《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》
  • 202009_风二西_USB鼠标流量
  • virtuoso默认设置
  • CF547D Mike and Fish
  • Tarjan vDCC 缩点
  • ABC_419_F - All Included
  • 软件工程第一次作业-自我介绍
  • DIFY 与 LangChain
  • VMware CentOS 7 `yum` 修复及 VMware Tools 安装问题复盘
  • 接口测试---Requests
  • LangChain大模型应用开发介绍
  • [豪の学习笔记] 软考中级备考 基础复习#8
  • lc1025-除数博弈
  • 漏洞解析--文件包含漏洞究竟怎么用?
  • 办公室装修 暂存
  • 博客更新公告
  • 爆:GitHub Copilot支持包括Anthropic、Azure、Google Gemini、Groq、OpenAI 和 OpenRouter等供应商API
  • JavaWeb05 - 详解
  • CF182C
  • CF185D
  • Python计算文件md5
  • CF201C
  • CF1774D
  • CF23C
  • CF37C
  • CF33D
  • 支持类 Unix 语法 ``:Windows 下用 PowerShell 7 优化 npm 和 VS Code
  • 初赛程序阅读做题要点