【BestCoder Round 65C】【树状数组 动态查找第k大 O(nlogn)】ZYB's Premutation 告诉你前i个数中的逆序对数让你还原全排列

本文主要是介绍【BestCoder Round 65C】【树状数组 动态查找第k大 O(nlogn)】ZYB's Premutation 告诉你前i个数中的逆序对数让你还原全排列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ZYB's Premutation

Accepts: 218
Submissions: 983
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
ZYBZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列.(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当A_i>A_jAi>Aj
输入描述
第一行一个整数TT表示数据组数。接下来每组数据:第一行一个正整数NN,描述排列的长度.第二行NN个正整数A_iAi,描述前缀区间[1,i][1,i]的逆序对数.数据保证合法.1 \leq T \leq 51T5,1 \leq N \leq 500001N50000
输出描述
TT行每行NN个整数表示答案的排列.
输入样例
1
3
0 1 2
输出样例
3 1 2


【BestCoder Round 65C】【树状数组 动态查找第k大 O(n(logn)^2)】ZYB's Premutation 告诉你前i个数中的逆序对数让你还原全排列

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=5e4+10,M=0,Z=1e9+7,ms63=1061109567;
int casenum,casei;
int a[N];
int b[N];
int ans[N];
int n;
void add(int x,int v)
{for(;x<=n;x+=x&-x)b[x]+=v;
}
int cnt(int x)
{int tmp=0;for(;x;x-=x&-x)tmp+=b[x];return tmp;
}
int main()
{scanf("%d",&casenum);for(casei=1;casei<=casenum;++casei){MS(b,0);scanf("%d",&n);for(int i=1;i<=n;++i){scanf("%d",&a[i]);//记录有多少个数比这个数大add(i,1);}for(int i=n;i>=1;--i){int kth=i-(a[i]-a[i-1]);int l=1;int r=n;while(l<r){int m=(l+r)>>1;if(cnt(m)>=kth)r=m;else l=m+1;}ans[i]=l;add(l,-1);}for(int i=1;i<n;++i)printf("%d ",ans[i]);printf("%d\n",ans[n]);}return 0;
}
/*
【题意】
告诉你一个1~n(1<=n<=50000)的全排列。
然后,告诉你前i个数中总共有多少个逆序对。
让你还原这个全排列。【类型】
树状数组+二分【分析】
告诉你前i个数共有几个逆序对,事实上就告诉了你,第i个数和之前的数共计形成了多少个逆序对。
也就是说,我们就知道了,对于第i个数,它在前i个数中排第几。
然而,我们发现这个很多时候并没有什么卵用,我们还是确定不了数值。除了——最后一个数。最后一个数在所有数中排第几,我们就能知道这个数是几。
然后,因为后面所有数都不涉及到这最后一个数的信息,于是我们把最后一个数从我们当前数的集合中抹去。
接下来,用同样的方法处理倒数第二个数,倒数第三个数,直到最后处理完所有的n个数。我们发现,为了实现刚才的想法,我们需要知道动态知道当前第几大的数是几。
这个我们可以通过线段树,用O(nlogn)实现。
也可以通过树状数组+二分,用O(n(logn)^2)实现。
就是二分最后一个数是m,看看前m个数中数的个数是否达到kth。
树状数组虽然多了一个logn,但是常数很小,所以一样可以快速AC。然而,接下来还要为大家介绍一种O(nlogn)的树状数组做法。~~【时间复杂度&&优化】
O(n(logn)^2) ->O(nlogn)【数据】
0 1 2 3 4
0 1 3 6 10*/

【BestCoder Round 65C】【树状数组 动态查找第k大 O(nlogn)】ZYB's Premutation 告诉你前i个数中的逆序对数让你还原全排列

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=5e4+10,M=0,Z=1e9+7,ms63=1061109567;
int casenum,casei;
int a[N];
int b[N];
int ans[N];
int n;
void add(int x,int v)
{for(;x<=n;x+=x&-x)b[x]+=v;
}
int cnt(int x)
{int tmp=0;for(;x;x-=x&-x)tmp+=b[x];return tmp;
}
int main()
{scanf("%d",&casenum);for(casei=1;casei<=casenum;++casei){MS(b,0);scanf("%d",&n);for(int i=1;i<=n;++i){scanf("%d",&a[i]);//记录有多少个数比这个数大add(i,1);}int top;for(top=1;top<=n;top<<=1);top>>=1;for(int i=n;i>=1;--i){int kth=i-(a[i]-a[i-1]);int p=0;int sum=0;for(int j=top;j;j>>=1)if(p+j<=n&&sum+b[p+j]<kth){p+=j;sum+=b[p];}++p;ans[i]=p;add(p,-1);}for(int i=1;i<n;++i)printf("%d ",ans[i]);printf("%d\n",ans[n]);}return 0;
}
/*
【题意】
告诉你一个1~n(1<=n<=50000)的全排列。
然后,告诉你前i个数中总共有多少个逆序对。
让你还原这个全排列。【类型】
树状数组+二分【分析】
告诉你前i个数共有几个逆序对,事实上就告诉了你,第i个数和之前的数共计形成了多少个逆序对。
也就是说,我们就知道了,对于第i个数,它在前i个数中排第几。
然而,我们发现这个很多时候并没有什么卵用,我们还是确定不了数值。除了——最后一个数。最后一个数在所有数中排第几,我们就能知道这个数是几。
然后,因为后面所有数都不涉及到这最后一个数的信息,于是我们把最后一个数从我们当前数的集合中抹去。
接下来,用同样的方法处理倒数第二个数,倒数第三个数,直到最后处理完所有的n个数。我们发现,为了实现刚才的想法,我们需要知道动态知道当前第几大的数是几。
这个我们可以通过线段树,用O(nlogn)实现。
也可以通过树状数组+二分,用O(n(logn)^2)实现。
就是二分最后一个数是m,看看前m个数中数的个数是否达到kth。
树状数组虽然多了一个logn,但是常数很小,所以一样可以快速AC。然而,接下来还要为大家介绍一种O(nlogn)的树状数组做法。~~
具体是怎样呢?
先回顾一下树状数组,以 +=x&-x 向上加权, -=x&-x 向下统计的做法为例。
我们会统计前1,2,4,8,16个数的权值到相应的计数桶里。
所以,如果我们要求前k大的,类似于倍增的做法,我们就跳着找——
前1个数内数的个数是否有k-1个
前2个数内数的个数是否有k-1个
前4个数内数的个数是否有k-1个
前8个数内数的个数是否有k-1个
如果没有,我们就计数+,而且把下标移过去,同时降低我们的倍增幅度(因为显然不会再倍增相同幅度)
如果有,我们就不计数+,不把下标移过去,但依然要降低我们的倍增幅度(因为我们这个幅度不能延伸,所以要看更小的范围)
直到我们得到最大的延展范围,使得范围内的数的个数恰好为k-1个。
于是,我们使得的下标向后移1,范围内数的个数就会恰好是k个。充分利用了树状数组的性质,使得做法是O(nlogn)【时间复杂度&&优化】
O(n(logn)^2) ->O(nlogn)【数据】
0 1 2 3 4
0 1 3 6 10*/


这篇关于【BestCoder Round 65C】【树状数组 动态查找第k大 O(nlogn)】ZYB's Premutation 告诉你前i个数中的逆序对数让你还原全排列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/610451

相关文章

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

Python动态处理文件编码的完整指南

《Python动态处理文件编码的完整指南》在Python文件处理的高级应用中,我们经常会遇到需要动态处理文件编码的场景,本文将深入探讨Python中动态处理文件编码的技术,有需要的小伙伴可以了解下... 目录引言一、理解python的文件编码体系1.1 Python的IO层次结构1.2 编码问题的常见场景二

linux查找java项目日志查找报错信息方式

《linux查找java项目日志查找报错信息方式》日志查找定位步骤:进入项目,用tail-f实时跟踪日志,tail-n1000查看末尾1000行,grep搜索关键词或时间,vim内精准查找并高亮定位,... 目录日志查找定位在当前文件里找到报错消息总结日志查找定位1.cd 进入项目2.正常日志 和错误日

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

C#高效实现Word文档内容查找与替换的6种方法

《C#高效实现Word文档内容查找与替换的6种方法》在日常文档处理工作中,尤其是面对大型Word文档时,手动查找、替换文本往往既耗时又容易出错,本文整理了C#查找与替换Word内容的6种方法,大家可以... 目录环境准备方法一:查找文本并替换为新文本方法二:使用正则表达式查找并替换文本方法三:将文本替换为图

Python中高级文本模式匹配与查找技术指南

《Python中高级文本模式匹配与查找技术指南》文本处理是编程世界的永恒主题,而模式匹配则是文本处理的基石,本文将深度剖析PythonCookbook中的核心匹配技术,并结合实际工程案例展示其应用,希... 目录引言一、基础工具:字符串方法与序列匹配二、正则表达式:模式匹配的瑞士军刀2.1 re模块核心AP

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到