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

离散化,分块。

预处理出:ans[i][j] 第i块到第j块的逆序对数。

f[i][j] 第1~i块中大于j的数的个数。

g[i][j] 第1~j块中小于j的数的个数。

每次询问时对于整块部分可以O(1)获得。

对于零散部分呢?

>在一列数的后面添加一个数,逆序对数会增加 数列中比它大的数的个数。

>在一列数的前面添加一个数,逆序对数会增加 数列中比它小的数的个数。

所以统计以上信息的时候,对于整块的部分,我们可以借由预处理的东西差分来O(1)地获得答案,零散的部分就是树状数组咯。

空间复杂度是O(n*sqrt(n))的。

时间复杂度是O(n*sqrt(n)*log(n))的。

P.S.根本不用卡常数什么的呢。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cmath>
  5 using namespace std;
  6 #define N 50001
  7 #define BLOCK_SIZE 250
  8 int sz,sum,n,a[N],l[BLOCK_SIZE],r[BLOCK_SIZE],D[N],f[BLOCK_SIZE][BLOCK_SIZE];
  9 int Less[BLOCK_SIZE][N],More[BLOCK_SIZE][N],b[N],en,m,x,y,num[N],ans;
 10 struct Point{int v,p;}t[N];
 11 bool operator < (const Point &a,const Point &b){return a.v<b.v;}
 12 int Res,Num;char C,CH[12];
 13 inline int G()
 14 {
 15     Res=0;C=‘*‘;
 16     while(C<‘0‘||C>‘9‘)C=getchar();
 17     while(C>=‘0‘&&C<=‘9‘){Res=Res*10+(C-‘0‘);C=getchar();}
 18     return Res;
 19 }
 20 inline void P(int x)
 21 {
 22     Num=0;if(!x){putchar(‘0‘);puts("");return;}
 23     while(x>0)CH[++Num]=x%10,x/=10;
 24     while(Num)putchar(CH[Num--]+48);
 25     puts("");
 26 }
 27 void add(int p,const int &v) {while(p<=n) {D[p]+=v; p+=(p&(-p));}}
 28 int getsum(int p) {int res=0; while(p) {res+=D[p]; p-=(p&(-p));} return res;}
 29 void makeblock()
 30 {
 31     sz=sqrt(n); if(!sz) sz=1;
 32     for(sum=1;sum*sz<n;sum++)
 33       {
 34           l[sum]=r[sum-1]+1;
 35           r[sum]=sum*sz;
 36           for(int i=l[sum];i<=r[sum];i++) num[i]=sum;
 37       }
 38     l[sum]=r[sum-1]+1;
 39     r[sum]=n;
 40     for(int i=l[sum];i<=r[sum];i++) num[i]=sum;
 41 }
 42 void LiSan()
 43 {
 44     sort(t+1,t+n+1); a[t[1].p]=++en;
 45     for(int i=2;i<=n;i++)
 46       {
 47           if(t[i].v!=t[i-1].v) en++;
 48           a[t[i].p]=en;
 49       }
 50 }
 51 void init_each_ans()
 52 {
 53     for(int i=1;i<=sum;i++)
 54       {
 55           memset(D,0,sizeof(D)); int pos=0,res=0;
 56           for(int j=i;j<=sum;j++)
 57             {
 58                 for(int k=l[j];k<=r[j];k++)
 59                   {
 60                       pos++;
 61                       add(a[k],1);
 62                       res+=(pos-getsum(a[k]));
 63                   }
 64                 f[i][j]=res;
 65             }
 66       } memset(D,0,sizeof(D));
 67 }
 68 void init_sum()
 69 {
 70     memcpy(b,a,sizeof(b));
 71     for(int i=1;i<=sum;i++)
 72       {
 73         sort(b+l[i],b+r[i]+1);
 74           memcpy(More[i],More[i-1],sizeof(More[i]));
 75           memcpy(Less[i],Less[i-1],sizeof(Less[i]));
 76           for(int j=1;j<=en;j++)
 77             {
 78                 More[i][j]+=(b+r[i]+1-upper_bound(b+l[i],b+r[i]+1,j));
 79                 Less[i][j]+=(lower_bound(b+l[i],b+r[i]+1,j)-(b+l[i]));
 80             }
 81       }
 82 }
 83 int getMore(const int &L,const int &R,const int &v){return More[R][v]-More[L-1][v];}
 84 int getLess(const int &L,const int &R,const int &v){return Less[R][v]-Less[L-1][v];}
 85 int main()
 86 {
 87     n=G();
 88     for(int i=1;i<=n;i++)
 89       {
 90           t[i].v=G();
 91           t[i].p=i;
 92       }
 93     LiSan(); makeblock(); init_each_ans(); init_sum();
 94     m=G();
 95     for(int j=1;j<=m;j++)
 96       {
 97           x=G(); y=G(); x^=ans; y^=ans;
 98           if(num[x]+1>=num[y])
 99             {
100                 int pos=0; ans=0;
101                 for(int i=x;i<=y;i++)
102                   {
103                       pos++;
104                       add(a[i],1);
105                       ans+=(pos-getsum(a[i]));
106                   }
107                 for(int i=x;i<=y;i++) add(a[i],-1);
108               P(ans);
109             }
110           else
111             {
112                 int pos=r[num[x]]-x+1; ans=f[num[x]+1][num[y]-1];
113                 for(int i=r[num[x]];i>=x;i--)
114                   {
115                       ans+=(getLess(num[x]+1,num[y]-1,a[i])+getsum(a[i]-1));
116                       add(a[i],1);
117                   }
118                 for(int i=l[num[y]];i<=y;i++)
119                   {
120                       pos++;
121                       add(a[i],1);
122                       ans+=(pos-getsum(a[i])+getMore(num[x]+1,num[y]-1,a[i]));
123                   }
124                 for(int i=x;i<=r[num[x]];i++) add(a[i],-1);
125                 for(int i=l[num[y]];i<=y;i++) add(a[i],-1);
126               P(ans);
127             }
128       }
129     return 0;
130 }
时间: 2024-10-16 05:07:07

【分块】【树状数组】bzoj3744 Gty的妹子序列的相关文章

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

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

hdu 5193 分块 树状数组 逆序对

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

BZOJ 3787 Gty的文艺妹子序列 分块+树状数组

题目大意:带修改.强制在线的区间逆序对 将之前3744TLE了的某个做法重写了一发 把其中一些预处理改成了树状数组 不得不说树状数组常数还是小啊 令g[i][j](i<=j)表示第i块中的元素与第i~j块中的元素之间的逆序对数 第一维暴力第二维树状数组维护前缀和 equals[i][j]表示前i块之内j的数量 这个直接暴力即可 smaller[i][j]表示前i块之内小于等于j的数的数量 第一维暴力第二维树状数组 修改时都维护一遍 查询时 首先我们把区间分为三块 令A为左侧零碎部分 B为中间成块

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

[BZOJ4167]永远的竹笋采摘 题解:我们考虑有多少点对(a,b)满足a与b的差值是[a,b]中最小的.以为是随机数据,这样的点对数目可能很少,实测是O(n)级别的,那么我们已知了有这么多可能对答案造成贡献的点对,如何将它们求出来呢? 考虑分块,因为所有数大小在[1,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 为何块套树要比

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

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

【XSY2111】Chef and Churus 分块 树状数组

题目描述 有一个长度为\(n\)的数组\(A\)和\(n\)个区间\([l_i,r_i]\),有\(q\)次操作: \(1~x~y\):把\(a_x\)改成\(y\) \(2~x~y\):求第\(l\)个区间到第\(r\)个区间的区间和的和. \(n,q\leq {10}^5,a_i\leq {10}^9\) 题解 分块. 设\(s_i\)为第\(i\)块的所有区间的区间和,\(d_{i,j}\)为第\(i\)块有多少个区间包含了\(j\)这个位置. 修改时修改树状数组和每个区间的区间和.设当前

【xsy2111】 【CODECHEF】Chef and Churus 分块+树状数组

题目大意:给你一个长度为$n$的数列$a_i$,定义$f_i=\sum_{j=l_i}^{r_i} num_j$. 有$m$个操作: 操作1:询问一个区间$l,r$请你求出$\sum_{i=l}^{r} f_i$. 操作2:将$a_x$变成$y$. 此题貌似正常做都不是很好做,考虑用一些奇奇怪怪的做法(比如说分块) 考虑到此题数列在不断地变化,我们考虑用树状数组来维护序列$a$,查询$f_i$的值可以在$O(log n)$的时间内完成. 如果这么做,单次询问的复杂度是$O(n log n)$的,

计蒜客 - A1998 Ka Chang (分块+树状数组)

题目链接 18年沈阳网赛的题,一直想补但一直鸽着,终于还是补上了 一棵树,点带权,支持两种操作: 1.深度d上的权值加上x 2.询问子树u下的权值和 对每个深度按结点数量分类,结点数小于$sqrt(n)$的为1类,其余的为2类 对于1类深度,修改时暴力修改每个结点的值,查询时用树状数组 对于2类深度,修改时直接在该深度上打上标记,查询时暴力枚举该结点子树下的每个深度超过$sqrt(n)$的结点 总复杂度$O((n+q)sqrt(n))$ 其实这道题本质上是一个二维平面上的区间修改查询问题,理论上