Codeforces 785 E. Anton and Permutation(分块,树状数组)

Codeforces 785 E. Anton and Permutation

题目大意:给出n,q。n代表有一个元素从1到n的数组(对应索引1~n),q表示有q个查询。每次查询给出两个数l,r,要求将索引为l,r的两个数交换位置,并给出交换后数组中的逆序对数。

思路:此题用到了分块的思想,即将这组数分为bsz块,在每一块上建Fenwick树,对于每次查询,只需要处理l,r中间的块和l,r所在块受影响的部分。具体实现见代码及注释。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<map>
#include<stack>
#include<queue>
#include<set>
#include<cmath>
#include<algorithm>
#include<climits>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef map<int,int> M;
typedef queue<int> Q;
typedef set<int> S;
typedef vector<int> V;
const int maxn=2e5+10,bsz=2000;
int n;
int br[maxn],bl[maxn],b[maxn]; //br[i]为编号为i块的右界,bl[i]为编号为i块的左界,b[i]为i点对应的块编号
struct fenwick
{
    int sum[maxn];
    void add(int p,int x)
    {
        while (p<=n)
        {
            sum[p]+=x;
            p+=p&-p;
        }
    }
    int qry(int p)
    {
        int res=0;
        while (p)
        {
            res+=sum[p];
            p-=p&-p;
        }
        return res;
    }
} fen[maxn/bsz+1];
int a[maxn];
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    int i,j,m,q;
    cin>>n>>q;
    //分块、定界
    int bn=(n-1)/bsz+1;
    for (i=0;i<bn;++i)
    {
        bl[i]=i*bsz;
        br[i]=min(n,i*bsz+bsz);
        for (j=bl[i];j<br[i];++j)
            b[j]=i;
    }
    //数据初始化,上树
    for (i=0;i<n;++i)
    {
        a[i]=i+1;
        fen[b[i]].add(a[i],1);
    }
    //处理query
    ll ans=0;
    while (q--)
    {
        int l,r;
        cin>>l>>r;
        l--;
        r--;
        if (l==r)
        {
            cout<<ans<<endl;
            continue;
        }
        else if (l>r)
            swap(l,r);
        int less_l=0,less_r=0;
        //处理l,r中间的块
        for (i=b[l]+1;i<b[r];++i)
        {
            less_l+=fen[i].qry(a[l]);
            less_r+=fen[i].qry(a[r]);
        }
        //处理l,r所在块的影响部分
        if (b[l]!=b[r])
        {
            for (i=l;i<br[b[l]];++i)
            {
                less_l+=a[i]<a[l];
                less_r+=a[i]<a[r];
            }
            for (i=bl[b[r]];i<=r;++i)
            {
                less_l+=a[i]<a[l];
                less_r+=a[i]<a[r];
            }
        }
        else
        {
            for (i=l;i<=r;++i)
            {
                less_l+=a[i]<a[l];
                less_r+=a[i]<a[r];
            }
        }
        //由于计算less_l和less_r时,对于端点l,r的处理有重复计数,因此需要修正答案
        if (a[l]<a[r])
            ans--;
        else
            ans++;
        //更新Fenwick树
         fen[b[l]].add(a[l],-1);
         fen[b[r]].add(a[r],-1);
         swap(a[l],a[r]);
         fen[b[l]].add(a[l],1);
         fen[b[r]].add(a[r],1);
        //处理答案
        int total=r-l;
        ll tmpl=(total-less_l)-less_l;//增加的逆序对数-减少的逆序对数
        ll tmpr=less_r-(total-less_r); //增加的逆序对数-减少的逆序对数
        ans+=tmpl+tmpr;
        cout<<ans<<endl;
    }
    return 0;
}
 

原文地址:https://www.cnblogs.com/orangee/p/9124058.html

时间: 2024-10-09 09:58:41

Codeforces 785 E. Anton and Permutation(分块,树状数组)的相关文章

Codeforces 220B - Little Elephant and Array 离线树状数组

This problem can be solve in simpler O(NsqrtN) solution, but I will describe O(NlogN) one. We will solve this problem in offline. For each x (0?≤?x?<?n) we should keep all the queries that end in x. Iterate that x from 0 to n?-?1. Also we need to kee

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间 [l,r]中妹子们美丽度的逆序对数吗?" 蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线." 请你帮助一下Autumn吧.

Codeforces 459D Pashmak and Parmida&#39;s problem(树状数组)

题目链接:Codeforces 459D Pashmak and Parmida's problem 题目大意:给定一个序列,定义f(l,r,x)为l≤k≤r并且ak=x的k的个数,求1≤i<j≤n的情况下,f(1,i,ai)<f(j,n,aj)的个数. 解题思路:预处理出f(1,i,ai),f(j,n,aj)值,然后用树状数组维护个数. #include <cstdio> #include <cstring> #include <algorithm> us

hdu 5193 分块 树状数组 逆序对

题意: 给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除.求每次修改后逆序对的个数. 限制: 1 <= n,m <= 20000; 1 <= ai <= n 思路: 插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个. 外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力.查找权值个数时,使用树状数组比较方便.内层通过树状数组维护权值. 每次更新即

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor

【BZOJ4167】永远的竹笋采摘 分块+树状数组

[BZOJ4167]永远的竹笋采摘 题解:我们考虑有多少点对(a,b)满足a与b的差值是[a,b]中最小的.以为是随机数据,这样的点对数目可能很少,实测是O(n)级别的,那么我们已知了有这么多可能对答案造成贡献的点对,如何将它们求出来呢? 考虑分块,因为所有数大小在[1,n]中,我们可以对于每个块,预处理出整个块到所有数的最小差值.然后从右往左枚举每一个点,再枚举右面所有的块,如果这个块到当前数的差值比之前的要小,那就暴力进入块中扫一遍.与此同时,我们需要知道是否已经存在这样的点对,被当前的点对

CF Educational Codeforces Round 10 D. Nested Segments 离散化+树状数组

题目链接:http://codeforces.com/problemset/problem/652/D 大意:给若干个线段,保证线段端点不重合,问每个线段内部包含了多少个线段. 方法是对所有线段的端点值离散化,按照左端点从大到小排序,顺着这个顺序处理所有线段,那么满足在它内部的线段一定是之前已经扫到过的.用树状数组判断有多少是在右端点范围内. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm>

BZOJ 2141 排队 分块+树状数组

题目大意:给定一个序列,m次交换两个数,求初始逆序对数及每次交换后的逆序对数 首先离散化,分块,对于每块建立一个树状数组,保存这个块中的所有元素 然后对于每个询问(x,y) (x<y) 两侧的数是没有影响的,区间(x,y)的数a[i]讨论如下: a[i]<a[x] --ans a[i]>a[x] ++ans a[i]<a[y] ++ans a[i]>a[y] --ans 然后对于块中的树状数组处理,块外的暴力 注意此题元素有重复 亲测可信 RANK5吓尿0.0 为何块套树要比

[P3759][TJOI2017]不勤劳的图书管理员(分块+树状数组)

题目描述 加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员.他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生 这两本书页数的和的厌烦度.现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置.因为小豆被要求在接下来的m天中至少 要整理一次图书.小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理. 输入输出格式 输入格式: 第一行会有两个数,n,m分别表示有n本书,m天 接下来n行,每行两