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

题目大意:带修改、强制在线的区间逆序对

将之前3744TLE了的某个做法重写了一发 把其中一些预处理改成了树状数组 不得不说树状数组常数还是小啊

令g[i][j](i<=j)表示第i块中的元素与第i~j块中的元素之间的逆序对数 第一维暴力第二维树状数组维护前缀和

equals[i][j]表示前i块之内j的数量 这个直接暴力即可

smaller[i][j]表示前i块之内小于等于j的数的数量 第一维暴力第二维树状数组

修改时都维护一遍 查询时 首先我们把区间分为三块

令A为左侧零碎部分 B为中间成块部分 C为右侧零碎部分

BB直接调用g数组 时间复杂度O(√nlogn)

AB利用equals和smaller数组求出对于A中每个元素B中比该元素小的数的数量 时间复杂度O(√nlogn)

BC利用B部分大小和smaller数组求出对于C中每个元素B中比该元素大的数的数量 时间复杂度O(√nlogn)

然后把A部分和C部分都放在一起 上BIT(我偷懒写了Merge_Sort) AA、AC、CC就都出来了

总时间复杂度不超过O(n√nlogn)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
#define SQRT_M 250
using namespace std;
int n,m,block,ans;
int a[M],b[M];
int f[SQRT_M][SQRT_M];
//f[i][i]表示第i块内部的逆序对数
//f[i][j](i<j)表示第i块中的元素与第j块中的元素之间的逆序对数
int g[SQRT_M][SQRT_M];
//g[i][j](i<=j)表示第i块中的元素与第i~j块中的元素之间的逆序对数
//第一维暴力第二维树状数组前缀和
//向上修改向下查询
int equals[SQRT_M][M];
int smaller[SQRT_M][M];
/*
equals[i][j]表示前i块之内j的数量 直接暴力
smaller[i][j]表示前i块之内小于等于j的数的数量 向上修改向下查询
第一维暴力第二维树状数组
*/
int l[SQRT_M],r[SQRT_M],belong[M];
int Merge_Sort(int l,int r,int b[]=::b)
{
    static int c[M];
    int i,mid=l+r>>1,re=0;

    if(l==r)
        return 0;
    re=Merge_Sort(l,mid,b)+Merge_Sort(mid+1,r,b);
    int l1=l,l2=mid+1;
    for(i=l;i<=r;i++)
    {
        if( b[l1]<=b[l2] && l1<=mid || l2>r )
            c[i]=b[l1++],re+=l2-mid-1;
        else
            c[i]=b[l2++];
    }
    memcpy( b+l , c+l , sizeof(b[0])*(r-l+1) );
    return re;
}
void Update(int c[],int x,int y,int limit)
{
    for(;x<=limit;x+=x&-x)
        c[x]+=y;
}
int Get_Ans(int c[],int x)
{
    int re=0;
    for(;x;x-=x&-x)
        re+=c[x];
    return re;
}
void Modify(int x,int y)
{
    int i,j,_block=belong[x],temp;
    for(i=1;i<_block;i++)
    {
        temp=upper_bound(b+l[i],b+r[i]+1,y)-upper_bound(b+l[i],b+r[i]+1,a[x]);
        Update(g[i],_block,-temp,(n-1)/block+1);
    }
    for(i=_block+1;(i-1)*block+1<=n;i++)
    {
        temp=lower_bound(b+l[i],b+r[i]+1,y)-lower_bound(b+l[i],b+r[i]+1,a[x]);
        Update(g[_block],i,temp,(n-1)/block+1);
    }
    memcpy(b+l[_block],a+l[_block],sizeof(b[0])*(r[_block]-l[_block]+1) );
    b[x]=y;
    temp=Merge_Sort(l[_block],r[_block]);
    //重构b数组
    Update(g[_block],_block,temp-f[_block][_block],(n-1)/block+1);
    f[_block][_block]=temp;
    //修改f[i][i]和g[i][~]
    for(i=_block;(i-1)*block+1<=n;i++)
    {
        equals[i][a[x]]--,equals[i][y]++;
        Update(smaller[i],a[x],-1,n);
        Update(smaller[i],y,1,n);
    }
    //修改euqal和smaller数组
    a[x]=y;
    //修改a数组
}
int Query(int x,int y)
{
    static int c[M];
    int i,top=0,re=0;
    if(belong[y]-belong[x]<=1)
    {
        memcpy(c+x,a+x,sizeof(a[0])*(y-x+1) );
        return Merge_Sort(x,y,c);
    }
    for(i=belong[x]+1;i<belong[y];i++)
        re+=Get_Ans(g[i],belong[y]-1);
    //BB
    for(i=x;i<=r[belong[x]];i++)
    {
        re+=Get_Ans(smaller[belong[y]-1],a[i])-Get_Ans(smaller[belong[x]],a[i])-
            ( equals[belong[y]-1][a[i]] - equals[belong[x]][a[i]] );
        c[++top]=a[i];
    }
    //AB
    for(i=l[belong[y]];i<=y;i++)
    {
        re+=r[belong[y]-1]-r[belong[x]]-
            ( Get_Ans(smaller[belong[y]-1],a[i])-Get_Ans(smaller[belong[x]],a[i]) );
        c[++top]=a[i];
    }
    //BC
    return re+Merge_Sort(1,top,c);
    //AA+AC+CC
}
int main()
{
    int i,j,k;
    int p,x,y;
    cin>>n;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    block=static_cast<int>(sqrt(n)+1e-7);

    for(i=1;i<=n;i++)
    {
        belong[i]=(i-1)/block+1;
        b[i]=a[i];
    }
    for(i=1;(i-1)*block+1<=n;i++)
    {
        l[i]=(i-1)*block+1,r[i]=min(i*block,n);
        f[i][i]=Merge_Sort(l[i],r[i]);
    }
    //预处理块和f[i][i]

    for(i=1;(i-1)*block+1<=n;i++)
        for(j=i+1;(j-1)*block+1<=n;j++)
        {
            int p=l[j];
            for(k=l[i];k<=r[i];k++)
            {
                for(;p<=r[j]&&b[p]<b[k];p++);
                f[i][j]+=p-l[j];
            }
        }
    for(i=1;(i-1)*block+1<=n;i++)
        for(j=i;(j-1)*block+1<=n;j++)
            Update(g[i],j,f[i][j],(n-1)/block+1);
    //预处理f数组和g数组

    for(i=1;i<=n;i++)
        for(j=belong[i];j<=(n-1)/block+1;j++)
        {
            equals[j][a[i]]++;
            Update(smaller[j],a[i],1,n);
        }
    //预处理equals数组和smaller数组

    cin>>m;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&p,&x,&y);
        x^=ans;y^=ans;
        if(p==1)
            Modify(x,y);
        else
            printf("%d\n", ans=Query(x,y) );
    }
}
时间: 2024-10-10 16:21:06

BZOJ 3787 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 思路: 插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个. 外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力.查找权值个数时,使用树状数组比较方便.内层通过树状数组维护权值. 每次更新即

Apple Tree POJ - 3321 dfs序列构造树状数组(好题)

There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree. The tree has N forks which are connected by branches. Kaka numbers

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

【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)$的,

BZOJ 3289: Mato的文件管理[莫队算法 树状数组]

3289: Mato的文件管理 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 2399  Solved: 988[Submit][Status][Discuss] Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mat

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

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

BZOJ 2738 矩阵乘法 整体二分+二维树状数组

题目大意:给定一个矩阵,多次求某个子矩阵中的第k小 分块解法见 http://blog.csdn.net/popoqqq/article/details/41356899 <论除最小割外题目解法从来与题目名称无关系列> 整体二分 Solve(x,y,S)表示处理答案在[x,y]区间内的询问集合S 预先将所有数按照大小排序 每次将[1,mid]之间的数插入树状数组 然后对于分治内部的每一个询问 去树状数组中查询相应子矩阵的数值 如果小于等于k就划分到左集合S1 否则划分到右集合S2 然后Solv

BZOJ 3594 Scoi2014 方伯伯的玉米田 树状数组

题目大意:给定一个序列,可以选择k次区间并将区间内每个数都+1,求操作之后LIS的最大值 我的做法不是标解...5E的复杂度为何跑的飞起... 首先一个显而易见的结论就是我们选择的k次区间右端点都是n时才能保证最优 知道这个我们就可以DP了- - 令f[i][j]表示前i个数上升j次的最大LIS 那么有f[i][j]=max{f[k][l]|k<i,l<=j,a[k]+l<=a[i]+j}+1 看到三维偏序就可以用二维树状数组了- - 时间复杂度O(nklog(max(ai)+k)log