题解 CF785E 【Anton and Permutation】

一道简单分块题,题目要求的是逆序对数量,那么对于每一次交换位置\(l,r(l \le r)\)的操作,我们只需要判断区间\((L,R)\)中大于与小于两个端点的数字个数即可,可以考虑将每个块中进行排序再二分达到快速查询,时间复杂度为\(O(n\sqrt{n}\log{n})\)

具体见代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 2e5 + 500;

int len, num[N], block[N], gt[N], L[N], R[N];
long long ans = 0;

long long work(int x, int k)
{
    int l = L[k], r = R[k], res = R[k] + 1;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (block[mid] > x)
            res = mid, r = mid - 1;
        else 
            l = mid + 1;
    }
    return R[k] - res + 1;
}

void update(int l, int r)
{
    if (gt[l] == gt[r])//直接暴力枚举
    {
        for (int i = l + 1; i < r; i++)
        {
            if (num[r] > num[i])
                ans++;
            else if (num[r] < num[i])
                ans--;
            if (num[l] < num[i])
                ans++;
            else if (num[l] > num[i])
                ans--;
        }
        if (num[l] > num[r])
            ans--;
        else if (num[l] < num[r])
            ans++;
        swap(num[l], num[r]);
    }
    else 
    {
        int i = l, j = r;
        i++, j--;
        while (gt[i] == gt[l])
        {
            if (num[r] > num[i])
                ans++;
            else if (num[r] < num[i])
                ans--;
            if (num[l] < num[i])
                ans++;
            else if (num[l] > num[i])
                ans--;
            i++;
        }
        while (gt[j] == gt[r])
        {
            if (num[r] > num[j])
                ans++;
            else if (num[r] < num[j])
                ans--;
            if (num[l] < num[j])
                ans++;
            else if (num[l] > num[j])
                ans--;
            j--;
        }
        for (int k = gt[i]; k <= gt[j]; k++)
        {
            ans += work(num[l], k);
            ans -= len - work(num[l], k);
            ans += len - work(num[r], k);
            ans -= work(num[r], k);
        }//二分找个数
        if (num[l] > num[r])
            ans--;
        else if (num[l] < num[r])
            ans++;
        swap(num[l], num[r]);
        for (int k = L[gt[r]]; k <= R[gt[r]]; k++)
            block[k] = num[k];
        for (int k = L[gt[l]]; k <= R[gt[l]]; k++)
            block[k] = num[k];
        sort(block + L[gt[l]], block + R[gt[l]] + 1);
        sort(block + L[gt[r]], block + R[gt[r]] + 1);//更行块中的数值
    }
}

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    len = sqrt(n);
    for (int i = 1; i <= n; i++)
        gt[i] = (i - 1) / len + 1, block[i] = i, num[i] = i;
    for (int i = 1; i <= gt[n]; i++)
    {
        L[i] = (i - 1) * len + 1, R[i] = i * len;
        sort(block + L[i], block + R[i] + 1);
    }//预处理区间端点
    while (k--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        if (l > r)
            swap(l, r);
        update(l, r);//进行更新
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2021-02-01 20:15  DSHUAIB  阅读(75)  评论(0编辑  收藏  举报