2018.06.29 NOIP模拟 排列(线段树)
排列(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模拟 排列(线段树)的更多相关文章
- 2018.06.29 NOIP模拟 旅馆(线段树)
旅馆 [问题描述] OIEROIEROIER 们最近的旅游计划,是到长春净月潭,享受那里的湖光山色,以及明 媚的阳光.你作为整个旅游的策划者和负责人,选择在潭边的一家著名的旅馆住 宿.这个巨大的旅馆一 ...
- 2018.06.29 NOIP模拟 Gcd(容斥原理)
Gcd 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出n个正整数,放入数组 a 里. 问有多少组方案,使得我从 n 个数里取出一个子集,这个子集的 gcd 不为 1 ,然后我再从 ...
- 2018.06.29 NOIP模拟 边的处理(分治+dp)
边的处理(side.cpp) [问题描述] 有一个 n 个点的无向图,给出 m 条边,每条边的信息形如<x,y,c,r><x,y,c,r><x,y,c,r>. 给出 ...
- 2018.06.29 NOIP模拟 Minimum(最小生成树)
Minimum 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出一幅由 n 个点 m 条边构成的无向带权图. 其中有些点是黑点,另外点是白点. 现在每个白点都要与他距离最近的所有黑 ...
- 2018.06.29 NOIP模拟 1807(简单递推)
1807 题目背景 SOURCE:NOIP2015-SHY-2 题目描述 给出一个由数字('0'-'9')构成的字符串.我们说一个子序列是好的,如果他的每一位都是 1.8.0.7 ,并且这四个数字按照 ...
- 2018.06.29 NOIP模拟 繁星(前缀和)
繁星 [问题描述] 要过六一了,大川正在绞尽脑汁想送给小伙伴什么礼物呢.突然想起以前拍过一张夜空中的繁星的照片,这张照片已经被处理成黑白的,也就是说,每个像素只可能是两个颜色之一,白或黑.像素(x,y ...
- 2018.06.29 NOIP模拟 区间(前缀和差量)
区间(interval.cpp) 时限:2000ms 空间限制:512MB [问题描述] 给出一个长度为 n 的序列 a[1]-a[n]. 给出 q 组询问,每组询问形如<x,y>< ...
- 2018.06.26 NOIP模拟 纪念碑(线段树+扫描线)
题解: 题目背景 SOURCE:NOIP2015−GDZSJNZXSOURCE:NOIP2015-GDZSJNZXSOURCE:NOIP2015−GDZSJNZX(难) 题目描述 2034203420 ...
- 2018.06.27 NOIP模拟 节目(支配树+可持久化线段树)
题目背景 SOURCE:NOIP2015-GDZSJNZX(难) 题目描述 学校一年一度的学生艺术节开始啦!在这次的艺术节上总共有 N 个节目,并且总共也有 N 个舞台供大家表演.其中第 i 个节目的 ...
随机推荐
- 【原】解决Debug JDK source 无法查看局部变量的问题方案(重新编译rt.jar包)
一.问题阐述 首先我们要明白JDK source为什么在debug的时候无法观察局部变量,因为在jdk中,sun对rt.jar中的类编译时,去除了调试信息,这样在eclipse中就不能看到局部变量的值 ...
- DDoS攻防战 (一) : 概述
岁寒 然后知松柏之后凋也 ——论语·子罕 (此图摘自<Web脚本攻击与防御技术核心剖析>一书,作者:郝永清先生) DDoS,即 Distributed Denial of Servi ...
- Graylog安装配置
ES集群健康检测:curl -sXGET http://localhost:9200/_cluster/health?pretty=true | grep "status" | a ...
- adb连接过程中常见问题解决方法
在测试过程中经常会遇到需要使用adb连接服务器的问题,但是有时候经常会遇到连不上的情况,总结两种解决方式 1)error: unknown host service 此问题是由于端口号已经被占用了,可 ...
- mysql备份和还原命令
备份和还原数据库都是在未登录的前提下进行命令操作的: 1.备份表: mysqldump -u root -p dbname table1 table2 > D:\sqlback.sql 2.备份 ...
- js中函数的使用方式及回调函数
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- Syncthing搭建
现在貌似只有windows和linux比较号装. 安装 先从官网下载好Windows 32位版(我本本对应的系统版本)的Syncthing,解压后可以看到如下文件结构 Syncthing文件结构 ...
- Unicode、UTF8与UTF16
1 概念 Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案 UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式 ...
- At least one JAR was scanned for TLDs yet contained no TLDs.
Tomcat提示如下: At least one JAR was scanned for TLDs yet contained no TLDs. =========================== ...
- PHP下ajax跨子域的解决方案之document.domain+iframe
对于主域相同,子域不同,我们可以设置相同的document.domain来欺骗浏览器,达到跨子域的效果. 例如:我们有两个域名:www.a.com 和 img.a.com 在www.a.com下有 ...