您的位置:首页 > 其它

UVA 11525 好大好大的排列(线段树)

2016-03-10 19:03 225 查看
题目在这

题意:求1-k的排列中第n大的序列,题目给出n的计算方法:

n = si*(k-1)+s2*(k-2)…+sk*0!; 并给你s1~sk

**分析:**n好大好大,,,,,

n是给的一个一个的阶乘和,想想:k位的排列数是k!

从0!到(k-1)!,,,似乎,对应着什么

每一个s[i]对应全排列的一位数字

(蚊子数学太弱,,,证明什么的,,,额)

不过看到一个一个阶乘还是可以yy出这么一个写法的:

每读入一个s[i],选取剩下的数中第s[i]+1小的,

有点拗口是吧,,,拿样例模拟模拟,就搞出来了

k很大,有5w,如果每次都赤裸裸的找的话,

平方复杂度必然TLE,开个线段树吧,

权值是0或1,1表示没被用过,0表示被用过了

维护区间和。。。。每次取剩下的第s[i]+1个数

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=140000;
int ans;

struct tree{
int su
,l
,r
,M;

void build(int n){
M=1; while(M<n)M<<=1; M--;
for (int i=M+1;i<=M+n;i++)l[i]=r[i]=i-M,su[i]=1;
for (int i=M+n+1;i<=M*2+1;i++)l[i]=r[i]=i-M,su[i]=0;
for (int i=M;i>=1;i--){
l[i]=l[i<<1],r[i]=r[i<<1^1];
su[i]=su[i<<1]+su[i<<1^1];
}
}

void get(int t,int k,int sum){
if (l[t]==r[t]){
ans=l[t];su[t]=0;return;
}
int mid=(l[t]+r[t])>>1;
if (sum+su[t<<1]>=k)get(t<<1,k,sum);
else get(t<<1^1,k,sum+su[t<<1]);
su[t]=su[t<<1]+su[t<<1^1];
}
}T;

int main(){
int cas;scanf("%d",&cas);
while (cas--){
int n;scanf("%d",&n);
T.build(n);
for (int i=1;i<=n;i++){
int x;scanf("%d",&x);
T.get(1,x+1,0);
printf("%d",ans);
if (i<n)printf(" ");
}
puts("");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: