首页 > 代码库 > UVA_11525 树状数组的活用 二分
UVA_11525 树状数组的活用 二分
我们知道1——k有K!种排列,现在给定k和n,要你按字典序输出 第n种排列的数列
而且题目给的 n是 n=S1(k-1)!+S2(k-2)!+...+Sk-1*1!+Sk*0!(0=<Si<=k-i),
k最大为50000,直接算出来不可能,我发现这个n的表达式很有意思,明显(k-1)!就是表示第一次除第一个以外,剩下k-1个数的排列个数,乘一个S1就说明之前用了多少次这种排列了,根据这个 再手算了一下,发现就是,每次找第Sk个数,输出,当然已经访问过的数就要T出去
这很明显就是树状数组 (或者线段树的单点修改和查询)功能,先初始化每个都是大小为1,然后每次二分找第Sk个,找到之后,把该位置置为-1,消除该位的影响,继续找,继续输出即可。你看题目里给定的S的范围也就是顺应这个,故意把剩下几个数,S就恰好限定在这个里面
发现自己二分写的还不是很熟,一开始还搞了一下子,是用mid还是L做结果呢 最后在我的写法里被证明是用L做结果
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define N 50000+10using namespace std;int A[N];int n;int lowbit(int x){ return x&(-x);}void add(int x,int v){ while (x<=n){ A[x]+=v; x+=lowbit(x); }}void init(){ for (int i=1;i<=n;i++){ add(i,1); }}int query(int x){ int ret=0; while (x>0){ ret+=A[x]; x-=lowbit(x); } return ret;}int main(){ int t; scanf("%d",&t); while (t--) { scanf("%d",&n); init(); for (int i=1;i<=n;i++){ int a; scanf("%d",&a); int L=1,R=n,mid; while (L<R) { mid=(L+R)>>1; int tmp=query(mid); //if (tmp==a+1) break; if (tmp<a+1) L=mid+1; else R=mid; } //cout<<"@@"<<mid<<endl; add(L,-1); printf("%d",L); if (i<n) putchar(‘ ‘); } puts(""); } return 0;}
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。