UVA - 11525 Permutation

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

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

思路:首先我们明确,比如321是集合{1,2,3}的第几大的序列,从第一位开始3开头的话,那么显然这个序列的前面就一定会有1,2开头的学列,就是2*2!,依次类推我们就可以确定这个学列是第几大的了,但是要注意到用过的数将不再被我们考虑在内,现在这道题目是反过来了,可以琢磨一下si的含义是剩下没用的数中第(si+1)大的数,我们通过线段树来处理,0,1,分别代表没使用过和使用过

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
#define mid ((l+r)>>1)
const int MAXN = 100005;

int s[MAXN],k;
int T[MAXN*4];

void cal(int rt){
	T[rt] = T[lson] + T[rson];
}

void build(int rt, int l, int r){
	if (l == r){
		T[rt] = 1;
		return;
	}
	build(lson, l, mid);
	build(rson, mid+1, r);
	cal(rt);
}

int query(int rt, int l, int r, int x){
	if (l == r){
		T[rt] = 0;
		return l;
	}
	int ans;
	if (x <= T[lson])
		ans = query(lson, l, mid, x);
	else ans = query(rson, mid+1, r, x-T[lson]);
	cal(rt);
	return ans;
}

int main(){
	int t;
	scanf("%d", &t);
	while (t--){
		scanf("%d", &k);
		build(1, 1, k);
		for (int i = 1; i <= k; i++)
			scanf("%d",&s[i]);
		for (int i = 1; i < k; i++)
			printf("%d ",query(1, 1, k, s[i]+1));
		printf("%d\n",query(1, 1, k, s[k]+1));
	}
	return 0;
}

UVA - 11525 Permutation

时间: 2024-10-27 05:49:18

UVA - 11525 Permutation的相关文章

UVA 11525 Permutation(树状数组)

题目意思是说  给你一个数k  然后有k个si   问你1--k 的第n个全排列是多少   注意是 1 2 3...k的全排列 不是si的 N=   由观察得知(k-i)!就是k-i个数字的全排列种数, 0=<Si<=k-i,所以显然可知假设当i==1时从第(k-1)!*s1到第n个全排列都是由第S1+1个数字开始的数列,因为每(k-1)!次排列过后,下一个排列的第1个数字都要增大1(每隔(k-1)!次,这k-1个数字都排列过一遍了,下一次只能增大更前面一个,也就是第1个了) 比如对于数列{1

uva 11525 - Permutation(线段树)

题目链接:uva 11525 - Permutation 题目大意:给定n和k,n给定的方式为k个si,根据公式计算出n,求一个由1~k组成的长度为k的序列的第n个排序 解题思路:根据公式的性质,等于对于每个位置找当前状态下第si小的数.线段树子节点均为1,维护和,查询时传入参数查找即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int ma

UVA 11525 Permutation ——(线段树,脑筋急转弯)

只要注意到对于譬如:S1*(k-1)! 因为后面k-1个数字的全排列个数刚好是(k-1)!,那么第一个数字就是没有取过的数字的第(S1+1)个即可.取走这个数字以后这个数字就不能再用了,依次类推即可得到整个排列了. 那么随便用线段树维护一下即可. 代码如下: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #define t_mid (l+r>>1) 5 #define

Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)

Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序,并将所有排列编号(从0开始),给出排列的编号得到对应排列)用到的式子.可以想到用逆康托展开的方法.但是需要一些变化: for(i=n;i>=1;i--) { s[i-1]+=s[i]/(n-i+1); s[i]%=(n-i+1); } 例如:n=3时,3=0*2!+0*1!+3*0!应该变为3=1

uva 1485 - Permutation Counting(递推)

题目链接:uva 1485 - Permutation Counting 题目大意:给定n和k,要求求一个由1~n组成的序列,要求满足ai>i的i刚好有k个的序列种数. 解题思路:dp[j][i]表示长度为i,j个位置满足的情况. dp[j+1][i]+=dp[j][i]?(j+1); 1, (3), (4), 2: 括号位置代表ai>i,既满足位置,此时i = 4, j = 2. -> 1, (3), (4), 2, 5 1 种,在最后追加 -> 1, (5), (4), 2,

uva 11922 - Permutation Transformer(伸展树)

题目链接:uva 11922 - Permutation Transformer 题目大意:给定一个序列,每次操作取出区间a~b,翻转后放到末尾,随后输出序列. 解题思路:就是伸展树,对于每个节点设一个flip,表示是否为翻转转态.每次将a旋转到根,然后分裂,再将b翻转到根,分裂,然后将mid翻转放到最后. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; str

UVA 11525

(一种通过康托展开求排列的方法) Problem 给定一个1-K的排列的康托展开形式,即S1*(K-1)! + S2*(K-2)! + S3*(K-3)! +......+ SK(K-K)!:输出排列. Limits Time Limit(ms); 3000 Memory Limit(MB): No Limit K: [1, 50000] Si: [0, K - i] Solution 根据康托展开的定义,可以看出,Si 表示的是当前比排列 i 位置的数小且没有在[1, i-1] 位置出现过的数

UVA - 1485 Permutation Counting

Description Given a permutation a1, a2,...aN of {1, 2,..., N}, we define its E-value as the amount of elements where ai > i. For example, the E-value of permutation {1, 3, 2, 4} is 1, while the E-value of {4, 3, 2, 1} is 2. You are requested to find

uva 11525(单点修改)

题意:有一个由1到k组成的序列,最小是1 2 - k,最大是 k k-1 - 1,给出n的计算方式,n = s0 * (k - 1)! + s1 * (k - 2)! +- + sk-1 * 0!,给出s1-sk,输出序列里第n大的序列. 题解:通过找规律发现结果是可以递推的,比如第三组样例: 4 2 1 1 0 那么n = 2 * 3! + 1 * 2! + 1 * 1! + 0 * 0! = 15,通过把1~4的排列全写下来,发现3!是更换第一个数字的间隔,2!是更换第二个数字的间隔,以此类