[bzoj2141][排队] (分块大法好)

Description

排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足ihj的(i,j)数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

Input

第一行为一个正整数n,表示小朋友的数量;第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;第三行为一个正整数m,表示交换操作的次数;以下m行每行包含两个正整数ai和bi¬,表示交换位置ai与位置bi的小朋友。

Output

输出文件共m行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度。

Sample Input

【样例输入】

3
130 150 140
2
2 3
1 3

Sample Output

1
0
3

【样例说明】
未进行任何操作时,(2,3)满足条件;
操作1结束后,序列为130 140 150,不存在满足ihj的(i,j)对;
操作2结束后,序列为150 140 130,(1,2),(1,3),(2,3)共3对满足条件的(i,j)。
【数据规模和约定】
对于100%的数据,1≤m≤2*103,1≤n≤2*104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。

Solution

分块啊,用块套树状数组,分块处理逆序对

考虑交换,不完整块就暴力枚举更新完整的就用树状数组直接更新

//Kaiba_Seto 20170116
//otz cjkmao
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MaxN 20010
#define MaxBuf 1<<22
#define RG register
#define inline __inline__ __attribute__((always_inline))
#define Blue() (((S == T)&&(T=(S=B)+fread(B,1,MaxBuf,stdin),S == T)) ? 0 : *S++)

char B[MaxBuf],*S=B,*T=B;

inline void Rin(RG int &x) {
    x=0;RG int c=Blue(),f=1;
    for(; c<48||c>57; c=Blue())
        if(c==45)f=-1;
    for(; c>47&&c<58; c=Blue())
        x=(x<<1)+(x<<3)+c-48;
    x*=f; }

struct Pr{
    int fir,sec;
    bool operator < (const Pr &other) const {
        return fir < other.fir; } }b[MaxN];

int n,m,a[MaxN],block_size,c1[MaxN],c2[200][MaxN],ans;

inline void exc(RG int &x,RG int &y) {
    x^=y;
    y^=x;
    x^=y; }

inline void modify(RG int *C,RG int x,RG int d) {
    for(; x<=n; x+=x&(-x))
        C[x]+=d; }

inline int sum(RG int *C,RG int x) {
    RG int res=0;
    for(; x; x-=x&(-x))
        res+=C[x];
    return res; }

int main() {
    Rin(n);
    block_size=static_cast<int>(sqrt(n)+1e-6);
    for(RG int i=1; i<=n; i++)
        Rin(b[i].fir),b[i].sec=i;
    std::sort(b+1,b+1+n);
    for(RG int i=1; i<=n; i++) {
        static int top=0;
        if(b[i].fir != b[i-1].fir)
            ++top;
        a[b[i].sec]=top; }
    for(RG int i=n; i; i--)
        ans+=sum(c1,a[i]-1),modify(c1,a[i],1);
    for(RG int i=1; i<=n; i++)
        modify(c2[(i-1)/block_size],a[i],1);
    printf("%d\n",ans);
    Rin(m);
    while(m--) {
        RG int x,y,l,r,i;
        Rin(x),Rin(y);
        if(x > y)exc(x,y);
        l=(x-1)/block_size+1;
        r=(y-1)/block_size-1;
        if(l <= r) {
            for(i=l; i<=r; i++)
                ans=ans-sum(c2[i],a[x]-1)+sum(c2[i],n)-sum(c2[i],a[x])+sum(c2[i],a[y]-1)-sum(c2[i],n)+sum(c2[i],a[y]);
            for(i=x+1; i<=l*block_size; i++) {
                if(a[i] < a[x])ans--;
                if(a[i] > a[x])ans++;
                if(a[i] > a[y])ans--;
                if(a[i] < a[y])ans++; }
            for(i=(r+1)*block_size+1; i<y; i++) {
                if(a[i] < a[x])ans--;
                if(a[i] > a[x])ans++;
                if(a[i] > a[y])ans--;
                if(a[i] < a[y])ans++; }
        }
        else {
            for(i=x+1; i<y; i++) {
                if(a[i] < a[x])ans--;
                if(a[i] > a[x])ans++;
                if(a[i] > a[y])ans--;
                if(a[i] < a[y])ans++; } }
        if(a[x] < a[y])ans++;
        else if(a[x] > a[y])ans--;
        printf("%d\n",ans);
        modify(c2[(x-1)/block_size],a[x],-1);
        modify(c2[(x-1)/block_size],a[y],1);
        modify(c2[(y-1)/block_size],a[x],1);
        modify(c2[(y-1)/block_size],a[y],-1);
        exc(a[x],a[y]); }
    fclose(stdin);
    return 0; }

orz cjkmao/Mr.cjk.cat

时间: 2024-10-11 17:22:55

[bzoj2141][排队] (分块大法好)的相关文章

bzoj 1699: [Usaco2007 Jan]Balanced Lineup排队 分块

1699: [Usaco2007 Jan]Balanced Lineup排队 Time Limit: 5 Sec  Memory Limit: 64 MB Description 每天,农夫 John 的N(1 <= N <= 50,000)头牛总是按同一序列排队. 有一天, John 决定让一些牛们玩一场飞盘比赛. 他准备找一群在对列中为置连续的牛来进行比赛. 但是为了避免水平悬殊,牛的身高不应该相差太大. John 准备了Q (1 <= Q <= 180,000) 个可能的牛的

分块大法吼2

莫对大法,O(nsqrt(n))解决无修改的区间询问问题: czy的后宫3 [题目描述] 上次czy在机房妥善安排了他的后宫之后,他发现可以将他的妹子分为c种,他经常会考虑这样一个问题:在[l,r]的妹子中间,能挑选出多少不同类型的妹子呢? 注意:由于czy非常丧尸,所以他要求在所挑选的妹子类型在[l,r]中出现次数为正偶数,你懂得. 问题简述:n个数,m次询问,每次问[l,r]区间有多少个数恰好出现正偶数次 [输入格式] 第一行3个整数,表示n,c,m 第二行n个数,每个数Ai在[1,c]之间

BZOJ2141: 排队

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2141 分块加树状数组. 离散化之后,每一个块建一个树状数组.交换x,y,与x左边的数和y右边的数无关,只需处理>x,<y的数. 话说还可以用树套树来写,不过常数太大,比分块加树状数组慢. 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorith

bzoj2141排队(辣鸡但是好写的方法)

题意很明确,也非常经典: 一个支持查询 区间中比k大的数的个数 并且支持单点修改的序列 ——因为题意可以转化为:查询这两个数中比后者大的个数.比后者小的个数.比前者大的个数.比前者小的个数(根据这4个就能算出增加/减少了多少对逆序对)并且把两个数修改掉 于是就出现了 ——来自百度 一个二分就能解决套个卵蛋woc身为一个蒟蒻,表示没有一个写得出的 于是我就想了一个好写(Rank100+几乎T掉)的方法: 首先复制一份原数据,把一份分块,并且保证每一块中的单调(也就是调用sqrt(n)次排序) 然后

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 为何块套树要比

分块大法好

用分快来水“xjr考考你数据结构”真是再爽不过了 xjr考考你数据结构一:虽然有点慢(6724ms) #include<cstdio> #include<cmath> #include<cctype> #include<cstring> #include<algorithm> using namespace std; inline int read() { char ch=getchar();int sig=1,x=0; while(!isdig

bzoj2141: 排队 cdq分治

和动态逆序对有些类似  这题既可以以时间为第一维 也可以以x轴维第一维度   具体视题目而定 cdq分治只能求点对之间的影响  如果要变成整个序列的情况  那么可以用前缀和   一开始的原序列的id设置成0即可 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #

骗分大法之-----分块||迷之线段树例题a

什么是分块呢? 就是一种可以帮你骗到不少分的神奇的算法. 分块的写法有几种,我所知道的有①预处理②不预处理 不预处理的代码我看得一脸懵逼 所以我在这里就谈一下预处理的版本www 首先看一道题: 给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:操作1:把 给定 第k1 个数改为k2;操作2:查询 从第k1个数到第k2个数得最大值.(k1<=k2<=n) 所有的数都 <=100000 好的,如果我们搞遍历肯定超时到爆炸. 那么就要用到分块大法了 把这n个数分成若干块,然后每个块

BZOJ 2738 矩阵乘法 分块

题目大意:给定一个矩阵,多次求一个子矩阵中的第k小 正解:CDQ分治 不会 二维莫队? 不会 于是果断分块大法好(又是 我们将这n*n个数排序 分n次插入 每次插入n个 每次插入后 去链表上处理尚未出解的询问(我懒得写链表写了并查集) 如果当前询问的子矩阵内已经插入大于等于k个数 那么答案一定在当次插入的n个数中 暴力查找即可 时间复杂度O(n^3+nq) 好卡-- #include<cstdio> #include<cstring> #include<iostream>