排列(premu.cpp)

【题目描述】

对于一个 1 到 n 的排列,逆序数的定义为:排列中第 i 位 ai的逆序数就是 a1…ai-1中比

ai大的数的个数。另外用 pi表示 a1,…,ai的逆序数和(即 pi为逆序数的前缀和)。

若知道 n 和 pi,则就能求得原排列。

现在对于排列{ai},给出 n 和{pi},请你还原这个排列。

【输入格式】

第一行输入一个数正整数 n。

第二行输入 n 个正整数,表示 pi 。

【输出格式】

输出一行,共有 n 个数,表示排列 ai。

【样例输入】

3

0 1 2

【样例输出】

3 1 2

【样例解释】

原排列 ai 3 1 2

每个数 ai对应的逆序数 0 1 1

逆序数前缀和 pi 0 1 2

【数据范围】

对于前 10%的数据:n≤10。

对于前 30%的数据:n≤1,000。

对于 100%的数据:n≤100,000;pi≤100,000,000。


这道题比较easyeasyeasy,首先可以根据最后一位的逆序数确定最后一个数,然后倒着依次确定每个数。a[1..i]a[1..i]a[1..i]中,有 bibibi 个数比 a[i]a[i]a[i]大,则 a[i]a[i]a[i]为剩下可选择的 $i $个数中第 i−b[i]i-b[i]i−b[i]小。这里介绍几种比较经典的做法

第一种:二分+树状数组,这是理论上效率最不优秀的一种做法,然而由于树状数组的常数极小,所以跑起来甚至比某些常数大的nlognnlognnlogn的算法还要更快。

(某学长的)代码如下:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;

int n,pre,now,tmp,l,r,mid;
int a[100010],tree[100010];

void read(int &x)
{
	x=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch))
	{
		x=(x<<3)+(x<<1)+ch-'0';
		ch=getchar();
	}
}

int lowbit(int x)
{
	return x&(-x);
}

int get(int x)
{
	int ans=0;
	while(x>=1)
	{
		ans+=tree[x];
		x-=lowbit(x);
	}
	return ans;
}

void add(int x,int y)
{
	while(x<=n)
	{
		tree[x]+=y;
		x+=lowbit(x);
	}
}

int main()
{
	//freopen("premu.in","r",stdin);

	read(n);
	pre=0;
	for(int i=1;i<=n;++i)
	{
		read(now);
		a[i]=now-pre;
		pre=now;
	}
	for(int i=1;i<=n;++i) add(i,1);
	for(int i=n;i>=1;i--)
	{
		tmp=i-a[i];
		l=0,r=n;
		while(l+1<r)
		{
			mid=(l+r)>>1;
			if(get(mid)<tmp) l=mid;
			else r=mid;
		}
		a[i]=r;
		add(r,-1);
	}
	for(int i=1;i<=n;++i) cout<<a[i]<<" ";
	return 0;
}

第二种:线段树,直接维护当前逆序对数为000且最靠右的数组下标,这就是当前最大数的位置。

(ykykyk神仙的)代码如下:

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define LD (t<<1)
#define RD ((t<<1)|1)
#define INF 10000010
int n,p[N],a[N];
int l[N<<2],r[N<<2],minv[N<<2],pos[N<<2],lz[N<<2];
void pushdown(int t){
	if(l[t]==r[t])return;
	if(lz[t]){
		lz[LD]+=lz[t];
		lz[RD]+=lz[t];
		minv[LD]-=lz[t];
		minv[RD]-=lz[t];
		lz[t]=0;
	}
}
void pushup(int t){
	if(minv[LD]<minv[RD]){
		minv[t]=minv[LD];
		pos[t]=pos[LD];
	}else{
		minv[t]=minv[RD];
		pos[t]=pos[RD];
	}
}
void build(int t,int ll,int rr){
	if(ll>rr)return;
	l[t]=ll;r[t]=rr;
	if(ll==rr){minv[t]=p[ll];pos[t]=ll;return;}
	int mid=(ll+rr)>>1;
	build(LD,ll,mid);
	build(RD,mid+1,rr);
	pushup(t);
}
void modify1(int t,int pos){
	if(l[t]==r[t]){minv[t]=INF;return;}
	pushdown(t);
	int mid=(l[t]+r[t])>>1;
	if(pos<=mid)modify1(LD,pos);
	else modify1(RD,pos);
	pushup(t);
}
void modify2(int t,int ll,int rr){
	if(ll>r[t]||rr<l[t])return;
	pushdown(t);
	if(ll<=l[t]&&r[t]<=rr){
		lz[t]++;
		minv[t]--;
		return;
	}
	modify2(LD,ll,rr);
	modify2(RD,ll,rr);
	pushup(t);
}
int main(){
//	freopen("premu.in","r",stdin);
//	freopen("premu.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&p[i]);
	for(int i=n;i>=1;i--)p[i]-=p[i-1];
	build(1,1,n);
	for(int i=n;i>=1;i--){
		int tmp=pos[1];
		modify1(1,tmp);
		modify2(1,tmp+1,n);
		a[tmp]=i;
	}
	for(int i=1;i<=n;i++)printf("%d ",a[i]);
	return 0;
}

第三种:平衡树,将逆序对数转化为当前节点在还没有确定的数中的排名,用平衡树维护。

(zxy大佬的)代码如下:

#include<bits/stdc++.h>
#define re register
#define ll long long
#define itn int
using namespace std;
inline
ll getint(){
	ll num=0;
	char c;
	bool f=0;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')break;
	if(c=='-')f=1,c=getchar();
	for(;isdigit(c);c=getchar())
	num=(num<<1)+(num<<3)+c-'0';
	return f?-num:num;
}

inline
void outint(ll a){
	if(a<0)putchar('-'),a=-a;
	if(a>9)outint(a/10);
	putchar(a%10+'0');
}
const int INF=0x7ffffff;
int pool=1;
int rt=0;
struct node {
int lc, rc, key, pri, cnt, sze;
#define lc(x) t[x].lc
#define rc(x) t[x].rc
#define v(x) t[x].key
#define p(x) t[x].pri
#define c(x) t[x].cnt
#define s(x) t[x].sze
} t[200005];
inline void upt(const int &k) { s(k) = s(lc(k)) + s(rc(k)) + c(k); }
inline void Zig(int &k) {
int y = lc(k);
lc(k) = rc(y);
rc(y) = k;
s(y) = s(k);
upt(k);k = y;
}
inline void Zag(int &k) {
int y = rc(k);
rc(k) = lc(y);
lc(y) = k;
s(y) = s(k);
upt(k);
k = y;
}
inline void Insert(int &k, const int &key) {
if (!k) {
k = ++pool; v(k) = key; p(k) = rand();
c(k) = s(k) = 1; lc(k) = rc(k) = 0;
return ;
}
else ++s(k);
if (v(k) == key) ++c(k);
else if (key < v(k)) {
Insert(lc(k), key);
if (p(lc(k)) < p(k)) Zig(k);
} else {
Insert(rc(k), key);
if (p(rc(k)) < p(k)) Zag(k);
}
return ;
}
inline void Delete(int &k, const int &key) {
if (v(k) == key) {
if (c(k) > 1) --c(k), --s(k);
else if (!lc(k) || !rc(k)) k = lc(k) + rc(k);
else if (p(lc(k)) < p(rc(k))) Zig(k), Delete(k, key);
else Zag(k), Delete(k, key);
return ;
}
else --s(k);
if (key < v(k)) Delete(lc(k), key);
else Delete(rc(k), key);
return ;
}
inline int QueryRank(const int &key) {
int x = rt, res = 0;
while (x) {
if (key == v(x)) return res + s(lc(x)) + 1;
if (key < v(x)) x = lc(x);
else res += s(lc(x)) + c(x), x = rc(x);
}
return res;
}
inline int QueryKth(int k) {
int x = rt;
while (x) {
if (s(lc(x)) < k && s(lc(x)) + c(x) >= k) return v(x);
if (s(lc(x)) >= k) x = lc(x);
else k -= s(lc(x)) + c(x), x = rc(x);
}
return 0;
}
inline int QueryPre(const int &key) {
int x = rt, res = -INF;
while (x) {
if (v(x) < key) res = v(x), x = rc(x);
else x = lc(x);
}
return res;
}
inline int QuerySuf(const int &key) {
int x = rt, res = INF;
while (x) {
if (v(x) > key) res = v(x), x = lc(x);
else x = rc(x);
}
return res;
}

int n;
int ans[100005];
int pre[100005];
int rank[100005];
int main(){
	n=getint();
	for(int re i=1;i<=n;++i)pre[i]=getint();
	for(int re i=n;i>=1;--i)pre[i]=pre[i]-pre[i-1];
	for(int re i=1;i<=n;++i)rank[i]=i-pre[i];
	for(int re i=1;i<=n;++i)Insert(rt,i);
	for(int re i=n;i>=1;i--){
		ans[i]=QueryKth(rank[i]);
		Delete(rt,ans[i]);
	}
	for(int re i=1;i<=n;++i){
		outint(ans[i]),putchar(' ');
	}
	return 0;
}

第四种:线段树,思想同平衡树,具体实现就是维护一段最靠左的前缀和。

(本蒟蒻的)代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define N 100005
using namespace std;
inline long long read(){
	long long ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
	return ans*w;
}
int p[N],a[N],ans[N],n;
struct Node{int l,r,sum;}T[N<<2];
inline void pushup(int p){T[p].sum=T[lc].sum+T[rc].sum;}
inline void build(int p,int l,int r){
	T[p].l=l,T[p].r=r;
	if(l==r){T[p].sum=1;return;}
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(p);
}
inline void update(int p,int k){
	if(T[p].l==T[p].r){
		T[p].sum=0;
		return;
	}
	if(k<=mid)update(lc,k);
	else update(rc,k);
	pushup(p);
}
inline int query(int p,int v){
	if(T[p].l==T[p].r)return T[p].l;
	if(T[lc].sum>=v)return query(lc,v);
	else return query(rc,v-T[lc].sum);
}
int main(){
//	freopen("premu.in","r",stdin);
//	freopen("premu.out","w",stdout);
	n=read();
	for(int i=1;i<=n;++i)p[i]=read(),a[i]=i-(p[i]-p[i-1]);
	build(1,1,n);
	for(int i=n;i>=1;--i){
		ans[i]=query(1,a[i]);
		update(1,ans[i]);
	}
	for(int i=1;i<=n;++i)printf("%d ",ans[i]);
	return 0;
}

2018.06.29 NOIP模拟 排列(线段树)的更多相关文章

  1. 2018.06.29 NOIP模拟 旅馆(线段树)

    旅馆 [问题描述] OIEROIEROIER 们最近的旅游计划,是到长春净月潭,享受那里的湖光山色,以及明 媚的阳光.你作为整个旅游的策划者和负责人,选择在潭边的一家著名的旅馆住 宿.这个巨大的旅馆一 ...

  2. 2018.06.29 NOIP模拟 Gcd(容斥原理)

    Gcd 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出n个正整数,放入数组 a 里. 问有多少组方案,使得我从 n 个数里取出一个子集,这个子集的 gcd 不为 1 ,然后我再从 ...

  3. 2018.06.29 NOIP模拟 边的处理(分治+dp)

    边的处理(side.cpp) [问题描述] 有一个 n 个点的无向图,给出 m 条边,每条边的信息形如<x,y,c,r><x,y,c,r><x,y,c,r>. 给出 ...

  4. 2018.06.29 NOIP模拟 Minimum(最小生成树)

    Minimum 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出一幅由 n 个点 m 条边构成的无向带权图. 其中有些点是黑点,另外点是白点. 现在每个白点都要与他距离最近的所有黑 ...

  5. 2018.06.29 NOIP模拟 1807(简单递推)

    1807 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出一个由数字('0'-'9')构成的字符串.我们说一个子序列是好的,如果他的每一位都是 1.8.0.7 ,并且这四个数字按照 ...

  6. 2018.06.29 NOIP模拟 繁星(前缀和)

    繁星 [问题描述] 要过六一了,大川正在绞尽脑汁想送给小伙伴什么礼物呢.突然想起以前拍过一张夜空中的繁星的照片,这张照片已经被处理成黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑.像素(x,y ...

  7. 2018.06.29 NOIP模拟 区间(前缀和差量)

    区间(interval.cpp) 时限:2000ms 空间限制:512MB [问题描述] 给出一个长度为 n 的序列 a[1]-a[n]. 给出 q 组询问,每组询问形如<x,y>< ...

  8. 2018.06.26 NOIP模拟 纪念碑(线段树+扫描线)

    题解: 题目背景 SOURCE:NOIP2015−GDZSJNZXSOURCE:NOIP2015-GDZSJNZXSOURCE:NOIP2015−GDZSJNZX(难) 题目描述 2034203420 ...

  9. 2018.06.27 NOIP模拟 节目(支配树+可持久化线段树)

    题目背景 SOURCE:NOIP2015-GDZSJNZX(难) 题目描述 学校一年一度的学生艺术节开始啦!在这次的艺术节上总共有 N 个节目,并且总共也有 N 个舞台供大家表演.其中第 i 个节目的 ...

随机推荐

  1. UI5-文档-4.26-(Optional) Remote OData Service

    到目前为止,我们已经使用了本地JSON数据,但是现在我们将访问一个真正的OData服务来可视化远程数据. 用可公开获得的Northwind OData服务显示并替换发票模型的JSONModel类型,以 ...

  2. ArrayList 原理(2)

    1. 概述 关于Java集合的小抄中是这样描述的: 以数组实现.节约空间,但数组有容量限制.超出限制时会增加50%容量,用System.arraycopy()复制到新的数组,因此最好能给出数组大小的预 ...

  3. Kotlin语言学习笔记(5)

    委托模式(Delegation) 类的委托 interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fu ...

  4. JQUERY框架的优点与面试题

    1 你觉得 jquery 有哪些好处?jQuery 是轻量级的 javascript 框架强大的选择器出色的 DOM 操作的封装可靠的事件处理机制完善的 ajax 封装出色的浏览器的兼容性支持链式操作 ...

  5. ID3、C4.5和cart算法比较(转)

    转自:https://www.zhihu.com/question/27205203

  6. Java http请求和调用(二)

    http请求代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader ...

  7. oracle杀掉执行的死循环存储过程

    select * from v$db_object_cache where locks > 0 and pins > 0 and type='PROCEDURE'; select b.si ...

  8. IE低版本浏览器兼容问题

    head标签中填写如下代码 <meta name="renderer" content="webkit"/> <meta name=" ...

  9. Python frozenset() 函数

    Python frozenset() 函数  Python 内置函数 描述 frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素. 语法 frozenset() 函数语法: ...

  10. 转)安装svn服务器

    以下转载自:http://www.linuxidc.com/Linux/2015-01/111956.htm 安装 安装软件包: sudo apt-get install subversion 配置 ...