码迷,mamicode.com
首页 > 其他好文 > 详细

uva 11525(单点修改)

时间:2015-08-09 22:42:53      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

题意:有一个由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!是更换第二个数字的间隔,以此类推,那么2*3!明显就是以数字3也就是s0 + 1开头的一个序列,因为已经经过了以1开头和以2开头的两个序列,那么之后的就是以数字2也就是s1 + 1作为第2个数字的序列,照着这个规律可以发现:
1 2 3 4 找到了第s0 + 1个数字,3被使用
1 2 4 找到第s1 + 1个数字,2被使用
1 4 找到第s2 + 1个数字,4被使用
1 找到第s3 + 1个数字,1被使用
所以最终序列就是 3 2 4 1。
然后就是想到如果第s[i] + 1个数字如果未被使用就标记已使用,如果已经被标记就要往后找第一个未被使用的数字标记,可是n有50000,所以想到要用线段树来减少时间(被想到),线段树每个结点存的是所有子节点是否被标记的情况。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 50005;
int n, s[N], sum[N << 2];

void pushup(int k) {
    sum[k] = sum[k * 2] + sum[k * 2 + 1];
}

void build(int k, int left, int right) {
    if (left == right) {
        sum[k] = 1;
        return;
    }
    int mid = (left + right) / 2;
    build(k * 2, left, mid);
    build(k * 2 + 1, mid + 1, right);
    pushup(k);
}

int query(int k, int left, int right, int x) {
    if (left == right) {
        sum[k] = 0;
        return left;
    }
    int mid = (left + right) / 2, res;
    if (sum[k * 2] >= x)
        res = query(k * 2, left, mid, x);
    else
        res = query(k * 2 + 1, mid + 1, right, x - sum[k * 2]);
    pushup(k);
    return res;
}

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

uva 11525(单点修改)

标签:

原文地址:http://blog.csdn.net/hyczms/article/details/47379689

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!