zoukankan      html  css  js  c++  java
  • 离散化的写法

    example  POJ 2229

    Ultra-QuickSort
    Time Limit: 7000MS  Memory Limit: 65536K
     

    Description

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
    9 1 0 5 4 ,

    Ultra-QuickSort produces the output
    0 1 4 5 9 .

    Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

    Input

    The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

    Output

    For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

    Sample Input

    5
    9
    1
    0
    5
    4
    3
    1
    2
    3
    0
    

    Sample Output

    6
    0
    

    Source

    ---------------------------------------------
    话说这题配的图何解?
    --------------------------------------------
     
    Solution

    离散化+树状数组

    ---------------------

    1.  map

    TLE的姿势

    #include <cstdio>
    #include <algorithm>
    #include <map>
    #include <cstring>
    using namespace std;
    
    const int N(5e5+5);
    int bit[N];
    int a[N], b[N];
    long long ans;
    map<int,int> mp;
    int sum(int x){
        int res=0;
        for(; x; res+=bit[x], x-=x&-x);
        return res;
    }
    void add(int x, int n){
        for(; x<=n; bit[x]++, x+=x&-x);
    }
    int main(){
        for(int n; scanf("%d", &n), n;){
            for(int i=0; i<n; i++){
                scanf("%d", a+i);
                b[i]=a[i];
            }
            sort(b, b+n);
            mp.clear();
            int tot=0;
            for(int i=0; i<n; i++)
                if(!mp[b[i]]) mp[b[i]]=++tot;
            ans=0;
            memset(bit, 0, sizeof(bit));
            for(int i=0, id; i<n; i++){
                id=mp[a[i]];
                ans+=sum(n)-sum(id);
                add(id, tot);
            }
            printf("%lld
    ", ans);
        } 
    }

    注意加粗的几行,其实没必要排序,map本身就有序

     ans+=sum(n)-sum(id);

    这一句还可以优化

    ans+=i-sum(id-1);

    AC的姿势

    #include <cstdio>
    #include <algorithm>
    #include <map>
    #include <cstring>
    using namespace std;
    
    const int N(5e5+5);
    int bit[N], a[N];
    long long ans;
    map<int,int> mp;
    int sum(int x){
        int res=0;
        for(; x; res+=bit[x], x-=x&-x);
        return res;
    }
    void add(int x, int n){
        for(; x<=n; bit[x]++, x+=x&-x);
    }
    int main(){
        for(int n; scanf("%d", &n), n; mp.clear()){
            for(int i=0; i<n; i++){
                scanf("%d", a+i);
                mp[a[i]];
            }
            int tot=0;
            for(map<int,int>::iterator it=mp.begin(); it!=mp.end(); it++)
                it->second=++tot;
            ans=0;
            memset(bit, 0, sizeof(bit));
            for(int i=0, id; i<n; i++){
                id=mp[a[i]];
                ans+=i-sum(id-1);
                add(id, tot);
            }
            printf("%lld
    ", ans);
        } 
    }

    这样常数还是很大,3766 MS

    -----------------------

    2.  间接排序

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    
    const int N(5e5+5);
    int bit[N], a[N], id[N], Rank[N];
    long long ans;
    int sum(int x){
        int res=0;
        for(; x; res+=bit[x], x-=x&-x);
        return res;
    }
    void add(int x, int n){
        for(; x<=n; bit[x]++, x+=x&-x);
    }
    bool cmp(const int &i, const int &j){
        return a[i]<a[j];
    }
    int main(){
        for(int n; scanf("%d", &n), n;){
            for(int i=0; i<n; i++)
                scanf("%d", a+i);
    
            for(int i=0; i<n; i++)
                id[i]=i;
            sort(id, id+n, cmp);        
            for(int i=0; i<n; i++)
                Rank[id[i]]=i+1;
    
            ans=0;
            memset(bit, 0, sizeof(bit));
            for(int i=0; i<n; i++){
                ans+=i-sum(Rank[i]-1);
                add(Rank[i], n);
            }
            printf("%lld
    ", ans);
        } 
    }

    代码中,id[i]表示第i小的数下标,Rank[i]表示a[i]是第几小的(即a[i]的Rank)。

    注意:题目已明确输入是"a sequence of n distinct integers"

    对于一般情况

            for(int i=0; i<n; i++)
                Rank[id[i]]=i+1;

    应改成

            Rank[id[0]]=1;        
            for(int i=1; i<n; ){
                Rank[id[i]]=Rank[id[i-1]]+1;
                for(i++; i<n&&a[id[i]]==a[id[i-1]]; i++)
                    Rank[id[i]]=Rank[id[i-1]];
            }

    547 MS

    --------------------------------------

    3.  unique + 二分查找

    当然,这道题所有数都不相同,不必unique,代码中给出的是针对一般情况的写法

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N(5e5+5);
    int bit[N], a[N], b[N];
    long long ans;
    int sum(int x){
        int res=0;
        for(; x; res+=bit[x], x-=x&-x);
        return res;
    }
    void add(int x, int n){
        for(; x<=n; bit[x]++, x+=x&-x);
    }
    int main(){
        for(int n; scanf("%d", &n), n;){
            for(int i=0, v; i<n; i++){
                scanf("%d", a+i);
                b[i]=a[i];
            }
            sort(b, b+n);
            int *End=unique(b, b+n);
            ans=0;
            memset(bit, 0, sizeof(bit));
            for(int i=0, id; i<n; i++){
                id=lower_bound(b, End, a[i])-b;
                ans+=i-sum(id);
                add(id+1, n);
            }
            printf("%lld
    ", ans);
        } 
    }

    688 MS

    注意:

    id=lower_bound(b, End, a[i])-b;

    不能写成

    id=find(b, b+n, a[i])-b;

    find()的复杂度是O(n)

    -------------------------------------

    这道题还有一种更为巧妙(且美妙)的解法,不必离散化,请读者细细品味

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N(5e5+5);
    int bit[N];
    struct Node{
        int x, y;
    }a[N];
    long long ans;
    int sum(int x){
        int res=0;
        for(; x; res+=bit[x], x-=x&-x);
        return res;
    }
    void add(int x, int n){
        for(; x<=n; bit[x]++, x+=x&-x);
    }
    bool cmp(const Node &a, const Node &b){
        return a.x > b.x;
    }
    int main(){
        for(int n; scanf("%d", &n), n;){
            for(int i=0, v; i<n; i++){
                scanf("%d", &v);
                a[i]={v, i+1};
            }
            sort(a, a+n, cmp);
            ans=0;
            memset(bit, 0, sizeof(bit));
            for(int i=0; i<n; i++){
                ans+=sum(a[i].y);
                add(a[i].y, n);
            }
            printf("%lld
    ", ans);
        } 
    }

    这种做法通常称为在线转离线

    375 MS

  • 相关阅读:
    Ubuntu “Failed to fetch”错误的解决方法
    #ifndef 与#pragma once
    vs TODO list使用
    window脚本编写bat程序执行
    vtk 的qt插件编译
    git bash 下载加速
    条件欧几里得聚类 pcl::ConditionalEuclideanClustering
    ANY数据类型的使用
    《C#编程风格》还记得多少
    驼峰命名法则
  • 原文地址:https://www.cnblogs.com/Patt/p/4949314.html
Copyright © 2011-2022 走看看