hdu5592 ZYB's Premutation

ZYB's Premutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 635    Accepted Submission(s): 300


Problem Description
ZYB has a premutation P ,but he only remeber the reverse log of each prefix of the premutation,now he ask you to
restore the premutation.

Pair (i,j)(i<j) is considered as a reverse log if Ai>Aj is matched.
 

Input
In the first line there is the number of testcases T.

For each teatcase:

In the first line there is one number N .

In the next line there are N numbers Ai ,describe the number of the reverse logs of each prefix,

The input is correct.

1T5 , 1N50000
 

Output
For each testcase,print the ans.
 

Sample Input
  
  
1 3 0 1 2
 

Sample Output
  
  
3 1 2

首先,非常感谢磊神指点,基本上copy磊神的代码


题目的意思很清楚,相当于给个密码,然后解码,发现一个规律,比如例子给的3 1 2,每一位的逆序对数是0 1 2,倒过来将每一位与前一位做差得0  1 1,为什么要倒过来呢,因为你新加入一个数,比如说在3 1的基础上加入2,2的加入对于前面的逆序对数是没有影响的,所以后减前得到的值可以理解为是由于这个数的加入,在原先的基础上增加了多少个逆序对,也就是可以反向知道一个数在原来的所有数中排第几


比如0 1 2,倒序相减以后得到0 1 1,从后往前看,第二个1的意思是第三位是1 2 3中第1 + 1 = 2大的数就是2,现在确定了第三位自然要将其排除,然后看第二个1的意思是在

剩下的1 3中第1 + 1 = 2大的数即1,再排除一个,现在1 2 3这个集合中只剩下3了,然后最后一个0,意思是在剩下的当中第0 + 1 = 1大的数,刚好也只剩一个3,最后输出3

综上倒序输出的顺序是2 1 3,反过来就是原来的序列3 1 2


但是这个问题解决了以后还没完,数据规模是50000,直接线性扫描找第几大的数肯定会超时,然后要用一个线段树来将找第几大的数的复杂度降到O(logN)


线段树上的数字表示此区间有多少个数,取出来一个,区间内的数的个数要减一


#include <cstdio>
#include <iostream>
#include <cstring>
#define lson rt << 1, l, m
#define rson rt << 1 | 1, m + 1, r

using namespace std;

const int MAX = 50050;

int sum[MAX << 2];

int a[MAX];
int val[MAX];
int ans[MAX];

void PushUP(int rt) {
	sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void build(int rt, int l, int r) {
	if (l == r) {
		sum[rt] = 1;  //将每个叶子初始化为1
		return;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	PushUP(rt);
}

int query(int x, int rt, int l, int r) {
	if (l == r) {
		sum[rt] = 0;  //取出来这个数,将区间内的数减少1个
		return l;
	}
	int m = (l + r) >> 1;
	int ans;
	if (sum[rt << 1 | 1] >= x) {  //因为右面的大,所以先考察右面孩子节点
		ans = query(x, rson);
	}
	else {
		ans = query(x - sum[rt << 1 | 1], lson);
	}
	PushUP(rt);
	return ans;
}

int main()
{
	int T, N;
	cin >> T;
	while (T--) {
		scanf("%d", &N);
		build(1, 1, N);
		a[0] = 0;   //初始化边界
		for (int i = 1; i <= N; i++) {
			scanf("%d", &a[i]);
		}
		for (int i = N; i >= 1; i--) {
			int tem;
			tem = a[i] - a[i - 1];  //倒过来计算
			val[i] = query(tem + 1, 1, 1, N);  //注意tem + 1
		}
		for (int i = 1; i <= N; i++) {
			printf("%s%d", i == 1 ? "" : " ", val[i]);
		}
		puts("");
	}
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,HDU1622是一道关于二叉树的题目,要求读入一系列二叉树的节点信息,输出它们的层序遍历结果。如果输入的二叉树不完整或存在重复节点,则输出"not complete"。下面是Java的实现代码: ```java import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; public class Main { static class Node { int val; Node left, right; public Node(int val) { this.val = val; } } public static void main(String[] args) { Scanner sc = new Scanner(System.in); while (sc.hasNext()) { String s = sc.nextLine(); if (s.isEmpty()) { continue; } String[] nodes = s.split("\\s+"); Node root = new Node(Integer.parseInt(nodes[0].substring(1))); Queue<Node> queue = new LinkedList<>(); queue.offer(root); boolean isComplete = true; for (int i = 1; i < nodes.length - 1; i += 2) { Node cur = queue.poll(); if (!nodes[i].equals("()")) { cur.left = new Node(Integer.parseInt(nodes[i].substring(1))); queue.offer(cur.left); } else { isComplete = false; } if (!nodes[i + 1].equals("()")) { cur.right = new Node(Integer.parseInt(nodes[i + 1].substring(0, nodes[i + 1].length() - 1))); queue.offer(cur.right); } else { isComplete = false; } } if (!isComplete) { System.out.println("not complete"); continue; } StringBuilder sb = new StringBuilder(); queue.offer(root); while (!queue.isEmpty()) { Node cur = queue.poll(); sb.append(cur.val).append(" "); if (cur.left != null) { queue.offer(cur.left); } if (cur.right != null) { queue.offer(cur.right); } } System.out.println(sb.toString().trim()); } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值