排序

离散化

例题 Cinema
之前一直读错题 orz 今天补下

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2e5 + 5;

int a[maxn], b[maxn], num[maxn];
struct node{
    int id;
    int lan;
    int sub;
    bool operator < (const node & a) const {
        return a.lan < lan || (a.lan == lan && a.sub < sub);
    }
}mo[maxn];

int chk(int x, int cnt){
    int l = 1, r = cnt;
    while(l < r) {
        int mid = l + r >> 1;
        if(b[mid] >= x) r = mid;
        else l = mid + 1;
    }
    if(x != b[l]) return 0;
    else return l;
}

int main(){
    int n, m;
    cin >> n;
    for(int i = 1, t; i <= n; i ++ ) {
        cin >> a[i];
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    for(int i = 1; i <= n; i ++ ) 
        num[chk(a[i], n)] ++;
    cin >> m;
    for(int i = 1, pos; i <= m; i ++ ) {
        cin >> pos;
        mo[i].id = i;
        pos = chk(pos, n);
        pos ? mo[i].lan = num[pos] : 0;
    }
    for(int i = 1, pos; i <= m; i ++ ) {
        cin >> pos;
        pos = chk(pos, n);
        pos ? mo[i].sub = num[pos] : 0;
    }
    sort(mo + 1, mo + 1 + m);
    cout << mo[1].id << endl;
    return 0;
}

货仓选址问题
ps 一般固定点选中位数 如果 没有这个要求的话 那个平均数更好

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5 ;
int n, m, a[maxn];

int main() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    int sum = 0;
    sort(a + 1, a + 1 + n);
    int pos = a[(n + 1) / 2];
    for(int i = 1;i <= n; i ++ ){
        sum += abs(a[i] - pos);
    }
    cout << sum << endl;
    return 0;
}

七夕祭
这题和货舱选址 用中位数 并且 和 均分纸牌有关
首先我们 先想 对一个列 or 行 进行纸牌均分 而且他是成环的 我们可以考虑从k人隔开
为了让这个和最小 我们最好 选中位数(货舱选址) 所以这题就这样狗出来了

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 100000 + 5;

int N, M, T;
int c[2][maxn], a[maxn], s[maxn];

int main(){
    cin >> N >> M >> T;
    for(int i = 1; i <= T; i ++ ) {
        int x, y;
        cin >> x >> y;
        c[0][y] ++;
        c[1][x] ++;
    }
    if((T % N) != 0 && (T % M) != 0) {
        cout << "impossible" << endl;
        return 0;
    }
    int ans = 0, flag = 0, ok = 0;
    if((T % M) == 0){
        int TM = T / M;
        for(int i = 1; i <= M; i ++ ) {
            a[i] = c[0][i] - TM;
            s[i] = s[i - 1] + a[i];
        }
        sort(s + 1, s + 1 + M);
        int mid = s[M + 1 >> 1];
        for(int i = 1; i <= M; i ++ ) {
            ans += abs(s[i] - mid);
        }
        ok = 1;
    }
    if((T % N) == 0){
        int TM = T / N;
        for(int i = 1; i <= N; i ++ ) {
            a[i] = c[1][i] - TM;
            s[i] = s[i - 1] + a[i];
        }
        sort(s + 1, s + 1 + N);
        int mid = s[N + 1 >> 1];
        for(int i = 1; i <= N; i ++ ) {
            ans += abs(s[i] - mid);
        }
        flag = 1;
    }
    if(flag && ok) cout << "both " << ans << endl;
    else if(flag) cout << "row " << ans << endl;
    else cout << "column " << ans << endl;
    return 0;
}

动态中位数 对顶堆 我记得我以前那权值线段树水过一个各种中位数的题
开两个堆,一个大根堆,一个小根堆,大根堆用来存放已经扫描到的元素里面,从小到大排名为前一半的元素,小根堆放后一半比较大的数 每次查询小根堆堆顶即为中位数。

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e5 + 5 ;
int n, m;

priority_queue<int, vector<int>, greater<int> > minheap;
priority_queue<int, vector<int>, less<int> > maxheap;

int main() {
    int cas;
    cin >> cas;
    while(cas --) {
        while(!minheap.empty()) minheap.pop();
        while(!maxheap.empty()) maxheap.pop();
        cin >> m >> n;
        cout << m << " " << (n + 1) / 2 << endl;

        for(int i = 1, t; i <= n; i ++ ) {
            cin >> t;
            if(minheap.empty()) minheap.push(t);
            else if(minheap.top() < t) minheap.push(t);
            else maxheap.push(t);

            if(minheap.size() < maxheap.size()) minheap.push(maxheap.top()), maxheap.pop();
            else if(minheap.size() > maxheap.size() + 1) maxheap.push(minheap.top()), minheap.pop();

            if(i & 1) cout << minheap.top() << " ";
            if(i % 20 == 0) cout << endl;
        }
        cout << endl;
    }
    return 0;
}

k大数

#include <iostream>
using namespace std;
int n, a[500], k, ans, f;
int qsort(int left, int right) {
    if(left == right) return a[left];
    if(left > right) return 0;
    int l = left, r = right;
    int x = a[l];
    while(l < r) {
        while(l < r && a[r] < x) r--;
        if(l < r) a[l++] = a[r];
        while(l < r && a[l] > x) l++;
        if(l < r) a[r--] = a[l];
    }
    a[l] = x;
    if(l == k) return a[l];
    if(l < k) return qsort(l + 1, right);
    else return qsort(left, l - 1);
}
int main() {
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> a[i];
    printf("%d", qsort(1, n));
}

逆序对
1.归并

#include <iostream>
using namespace std;
const int maxn = 5e5 + 5 ;

int n, m;
int a[maxn], b[maxn];
int cnt;

void merges(int l, int r) {
    if(r - l < 1)
        return ;
    int mid=l + r >> 1;
    merges(l, mid);
    merges(mid + 1, r);
    int i = l, j = mid + 1;
    for(int k = l; k <= r; k++) {
        if(j > r || i <= mid && a[i] <= a[j])
            b[k] = a[i++];
        else
            b[k] = a[j++], cnt += mid - i + 1;
    }
    for(int k = l; k <= r; k++)
        a[k] = b[k];
}

signed main() {
    while(cin >> n, n) {
        cnt = 0;
        for(int i = 1; i <= n; i++)
            cin >> a[i];
        merges(1, n);
        cout << cnt << endl;
    }
    return 0;
}
  1. 树状数组
    数据大时离散化...
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 5e5 + 5 ;

int n, m;
int c[maxn];
int lowbit(int x) {
    return (-x) & x;
}

void add(int x, int y){
    while(x <= n) {
        c[x] += y;
        x += lowbit(x);
    }
}

int get_sum(int x) {
    int res = 0;
    while(x){
        res += c[x];
        x -= lowbit(x);
    }
    return res;
}

int main() {
    while(cin >> n, n) {
        int cnt = 0;
        memset(c, 0, sizeof(c));
        for(int i = 1, t; i <= n; i++){
            cin >> t;
            cnt += get_sum(n) - get_sum(t);
            add(t, 1);
        }
        cout << cnt << endl;
    }
    return 0;
}

奇数码问题 
ps:逆序对奇数和偶数 相同有解

倍增

天才ACM  倍增

归并完成对B的排序 chk从中找最大
如果 超过的话 还原B数组(A);
没有的话 保留排序 下一次就可以从nr开始排序 减低复杂度
倍增找到最长可行区间 使分段最少

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
int K, N, M, ans;
int A[maxn], B[maxn], C[maxn];
ll T;
 
bool chk(int cnt){
    ll res = 0;
    int p = 1,q = cnt, ms = M;
    while(p < q && ms --){
        ll s = C[q] - C[p];
        res += s * s;
        if(res > T) return false;
        p++, q--;
    }
    return true;
}
 
bool combine(int l,int r,int nr){
    sort(B + r + 1,B + nr + 1);
    int p = l,q = r + 1;
    int cnt = 0;
    while(p <= r && q <= nr){
        if(B[p] < B[q])  C[++ cnt] = B[p ++];
        else C[++ cnt] = B[q ++];
    }
    while(p <= r) C[++ cnt] = B[p ++];
    while(q <= nr) C[++ cnt] = B[q ++];
    if(chk(cnt)){
        int i = l;
        for(int i = l; i <= nr; i ++ ) A[i] = B[i] = C[i - l + 1];
        return true;
    }
    else{
        for(int i = r + 1;i <= nr;i++)  B[i] = A[i];
        return false;
    }   
}
 
void solve(){
    int l = 1,r = 1;
    ans = 0;
    while(l <= N){
        int nr,d = 1;
        while(d){
            nr =  r + d;
            if(nr <= N && combine(l,r,nr)) r = nr,d *= 2;
            else d /= 2;
        }
        ans++;
        r = l = r + 1;
    }
}
int main(){
    cin >> K;
    while(K --){
        cin >> N >> M >> T;
        for(int i = 1; i <= N; i ++) cin >> A[i], B[i] = A[i];
        solve();
        cout<<ans<<endl;
    }
    return 0;
}

ST表
https://www.luogu.org/problemnew/show/P3865

#include <cstdio>
#include <iostream>
using namespace std;

const int maxn = 1e5 + 5;

int lg[maxn];
int a[maxn], f[maxn][30];

int n, m;

void STbuild() {
    for(int i = 1; i <= n; i ++ ) f[i][0] = a[i];
    int t = (lg[n]-1)/(lg[2]-1) + 1;
    for(int j = 1; j < t; j ++ ) {
        for(int i = 1; i <= n - (1 << j) + 1; i ++ ) {
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int STquery(int l, int r){
    int k = (lg[r - l + 1] - 1);
    return max(f[l][k], f[r - (1 << k) + 1][k]);
}

int main() {
    for(int i = 1; i < maxn; i ++){
        lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
    }
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i ++){
        scanf("%d", &a[i]);
    }
    STbuild();
    for(int i = 1; i <= m; i ++) {
        int x, y;
        scanf("%d %d", &x, &y);
        printf("%d\n", STquery(x, y));
    }
    return 0;
}