排列(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. mysql 集群 数据同步

    mysql集群配置在网站负载均衡中是必不可少的: 首先说下我个人准备的负载均衡方式: 1.通过nginx方向代理来将服务器压力分散到各个服务器上: 2.每个服务器中代码逻辑一样: 3.通过使用redi ...

  2. node 删除文件 和文件夹

    删除文件 var fs = require('fs'); fs.unlink(path,callback); 删除文件夹 deleteFolder(path); function deleteFold ...

  3. setdeamon 设置 线程为守护线程, (lock线程锁, BoundedSemaphore,rlock递归锁 ) 三种锁

    1.setdeamon 当主程序执行完时,子程序自动被销毁 ,内存自动被收回 例一: import threading, time def run(n): print('run %s'%n) time ...

  4. as3 air 保存文本内容的换行

    private function newData() { var str:String="<root>"; for(var i in charCode_arr) { s ...

  5. VBox 安装 macOS 10.12

    安装步骤⑴ 下载及解压 macOS 10.12 Sierra Final by TechReviews.rar ⑵ 下载及双击安装 VirtualBox-5.1.6-110634-Win.exe ,默 ...

  6. Validate the date format

    Validate the date format function checkdate(input) { var validformat = /^\d{2}\/\d{2}\/\d{4}$/; //Ba ...

  7. js中函数传参的情况

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  8. ContextLoaderListener和Spring MVC中的DispatcherServlet学习

    DispatcherServlet介绍 DispatcherServlet是Spring前端控制器的实现,提供Spring Web MVC的集中访问点,并且负责职责的分派,与Spring IoC容器无 ...

  9. UnicodeEncodeError: ‘ascii’ codec can’t encode

    [UnicodeEncodeError: ‘ascii’ codec can’t encode] Python默认环境编码通过下面的方法可以获取: 基本上是ascii编码方式,由此Python自然调用 ...

  10. 读《asp.net MVC4开发指南(黄保翕编著)》笔记

    在刚刚过去的中秋节中,利用了两天的碎片时间把黄保翕编著的<asp.net MVC4 开发指南>看了遍,笔记如下,欢饮在开发MVC的同学一起来探讨: 1.社区 2.开源程序 3.易测试性 4 ...