SPOJ 227 Ordering the Soldiers

点击打开SPOJ227

思路: 树状数组

分析:

1  给定一个n个数的序列假设为b数组,那么b[i]表示的是i之前比第i个数大的个数,比如样例的2 1 3对应的b数组是0 1 0,现在要求a数组,已知a数组的值是1~n

2  我们通过b数组可以知道a数组的情况,因为前面的数会影响后面的数,那么我们从后面枚举b数组,这样我们可以知道对于第i个数i-b[i]就是剩下的i个数中的第几大的数,比如第二个样例

    0 1 2 0 1 , 那么i为5的时候i-b[i] = 5-1 = 4,说明第5的数是1~n中的第四大的数也就是4,接下来我们删除掉4,i为4的时候i-b[i] = 4-0 = 4也就是第四个数是剩下的第4大的数也就是5,以此类推.....

3  通过2的思路,我们发现很简单,但是我利用vector的时候TLE了,说明我们需要一个更高效率的算法

4  由于n个数是1~n,那么我们初始化一个树状数组treeNum[i] = i;

    表示的是前面有i个数比它小,那么我们可以知道树状数组是一个单调递增,为什么呢?因为我们初始化每个点的值都还没被取过,那么区间[1,i]的和为i,所以i越大和越大。

    通过第2点的分析我们可以知道,我们能够求出每一个数的排名,也就是前面比它小的个数,那么这个就是树状数组保存的值,所以我们可以通过二分答案来求,我们取了某个数之后就标为true,然后树状数组进行单点更新。

    但是我们会遇到一个问题就是比如第二个样例中我们把4和5取完之后我们会发现[1,3]和[1,4]和[1,5]这三个区间的和为3,但是只有3是符合的,因为4和5被取了,所以我们在二分的时候应该注意判断值是否被取过

5  那么我们来分析一下时间复杂度,我们需要枚举b数组为O(n),每次二分的时间为O(logn),每次更新树状数组的时间为O(logn),总的为O(n*logn*logn)

代码:

/***********************************************
* By: chenguolin                               *
* Date: 2013-08-19                             *
* Address: http://blog.csdn.net/chenguolinblog *
***********************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 200010;

int n;
int num[MAXN];
int treeNum[MAXN];
bool isSelect[MAXN];

int lowbit(int x){
    return x&(-x);
}

int getSum(int x){
    int sum = 0;
    while(x){
         sum += treeNum[x];
         x -= lowbit(x);
    }
    return sum;
}

void add(int x , int val){
    while(x < MAXN){
         treeNum[x] += val;
         x += lowbit(x);
    }
}

void init(){
    memset(isSelect , false , sizeof(isSelect));
    memset(treeNum , 0 , sizeof(treeNum));
    for(int i = 1 ; i <= n ; i++)
        add(i , 1);
}

int search(int x){
    int left = 1;
    int right = n;
    while(left <= right){
         int mid = (left+right)>>1;
         int sum = getSum(mid);
         if(sum == x){
             if(!isSelect[mid])
                 return mid;
             right = mid-1;
         }
         else if(sum < x)
             left = mid+1;
         else
             right = mid-1;
    }
}

void solve(){
    init();
    int output[MAXN];
    for(int i = n ; i >= 1 ; i--){
        int x = i-num[i];
        int ans = search(x);
        output[i] = ans;
        isSelect[ans] = true;
        add(ans , -1);
    }
    printf("%d" , output[1]);
    for(int i = 2 ; i <= n ; i++)
        printf(" %d" , output[i]);
    puts("");
}

int main(){
    int cas;
    scanf("%d" , &cas);
    while(cas--){
         scanf("%d" , &n);
         for(int i = 1 ; i <= n ; i++)
             scanf("%d" , &num[i]);
         solve();
    }
    return 0;
}
时间: 2024-11-05 20:32:27

SPOJ 227 Ordering the Soldiers的相关文章

树状数组专题【完结】

1 国外的论文点击打开链接 2 我的总结点击打开链接 任何能够造成树状数组下表为0的操作都会使得程序TLE,这是个很重要的地方! 第一题 poj 2299 Ultra-QuickSort 点击打开poj 2299 思路: 离散化+树状数组 分析: 1 题目的意思就是要求逆序数对 2 题目的输入个数有500000的规模但是每个数的最大值为999999999,因此我们需要离散化这些数据 3 对于数据9 1 0 5 4我们离散化成5 2 1 4 3 那么对于输入一个树a[i]我们去求一下它的离散化后的

Collection and Object Ordering

object .Net SDK provides a number of collection classes in the System.Collections namespace.We use these collection classes to store objects inside them and perform some operation on them later on as per application logic. Many times we need to re-or

SPOJ Problem Set 2. Prime Generator 求某区间质数题解

题目大意: 给定两个数,要求产生这两个数之间的所有质数. 两个数位m和n,其范围如下: The input begins with the number t of test cases in a single line (t<=10). In each of the next t lines there are two numbers m and n (1 <= m <= n <= 1000000000, n-m<=100000) separated by a space.

SPOJ 查找下一个回文Palindrome 算法题解

给出一个数值,well,其实不是数值了,而是一大串数字, 比如 98237482340328490328490324893024,非常长的数字. 找出下一个Palindrome,这个Palindrome在数值上要比当前数值大(不能等于). 如: 9 9 9 9->1 0 0 0 1 1 2 3 4 5 ->1 2 4 2 1 本算法时间效率是O(n),其中n是指数值的位数,不是数值,比如123456789,只有9位,那么本算法接近常数: 更多精彩内容:http://www.bianceng.c

UVa 10305 Ordering Tasks:拓扑排序模板

10305 - Ordering Tasks Time limit: 3.000 seconds http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=1246 模板题.注意倒着输出. 模板: 01./*0.022s*/ 02. 03.#include<bits/stdc++.h> 04.using na

spoj EDIST dp string distance

http://www.spoj.com/problems/EDIST/ #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> using namespace std; int f[2002][2002]; char a[2002], b[2002]; int main(){ int t; scanf("%d&q

奇怪了 我现在通过 http://122.227.164.81:8080/qwfymain/index.jsp 这个已经 可以访问 服务器发布的jsp网站了,可

问题描述 奇怪了我现在通过http://122.227.164.81:8080/qwfymain/index.jsp这个已经可以访问服务器发布的jsp网站了,可是连接到动态页面就报错:java.sql.SQLException:Accessdeniedforuser'root'@'localhost'(usingpassword:YES),网上说是什么权限问题,我试了都不行,我把本地的上项目,数据库连接改到服务器的数据库,测试发现没这个问题,谁知道这个问题怎么解决?我项目是struts2+spr

SPOJ 1029 Matrix Summation

点击打开SPOJ 1029 思路: 二维树状数组 分析: 1 简单的二维树状数组,注意因为数据量很大TLE了多次,之后把memset改成for循环A了 代码: #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAXN = 1100; int n; int mat[MAXN][MAXN]; int t

全国2347余万人享低保平均每人每月227.8元

人民网北京7月13日电 (记者 常红) 全国老龄委今日发布"2009年度中国老龄事业发展统计公报".公报指出,截至2009年底,全国城市低保对象2347.7万人,其中老年人333.5万人,占14.2%:全国平均低保标准每人每月227.8元,月人均补助165元,同比分别增长10.9%和17%.各地还积极推进分类施保措施,对丧失劳动能力.患病.残疾等特殊困难老年人提高补助水平,确保其基本生活.