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.
Ultra-QuickSort produces the output
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