BZOJ.2639.矩形计算(二维莫队)

题目链接

二维莫队,按x,y坐标一起分块.(x,y)的所属的块为 x/sq(n)*sq(m) + y/sq(m)
排序时按照(左下点所在块,右上点的标号)排序
排序后 先得出一个询问的答案,然后利用上一个询问的矩形与当前矩形位置关系更新答案
转移真的麻烦。。为了避免算重 一定要加个vis[][]

//4432kb    13600ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=205,M=1e5+5;

int n,m,Q,szX,szY,A[N][N],ref[N*N],tm[N*N],bel[N][N],idv[N][N];
LL Ans[M],Now;
bool vis[N][N];
struct Ask
{
    int x1,y1,x2,y2,id;
    inline bool operator <(const Ask &a)const{
        return bel[x1][y1]==bel[a.x1][a.y1]?idv[x2][y2]<idv[a.x2][a.y2]:bel[x1][y1]<bel[a.x1][a.y1];
    }
}q[M];

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
int Find(int x,int r)
{
    int l=1,mid;
    while(l<r)
        if(ref[mid=l+r>>1]>=x) r=mid;
        else l=mid+1;
    return l;
}
void Discrete()
{
    int tot=0,cnt=1;
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            ref[idv[i][j]=++tot]=A[i][j]=read();
    std::sort(ref+1,ref+1+tot);
    for(int i=2; i<=tot; ++i)
        if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            A[i][j]=Find(A[i][j],cnt);
}
inline void add(int i,int j){
    if(!vis[i][j]) vis[i][j]=1, Now+=2*(tm[A[i][j]]++)+1;
}
inline void subd(int i,int j){
    if(vis[i][j]) vis[i][j]=0, Now-=2*(--tm[A[i][j]])+1;
}
void Add(int x1,int x2,int y1,int y2)
{
    for(int i=x1; i<=x2; ++i)
        for(int j=y1; j<=y2; ++j) add(i,j);
}
void Subd(int x1,int x2,int y1,int y2)
{
    for(int i=x1; i<=x2; ++i)
        for(int j=y1; j<=y2; ++j) subd(i,j);
}
void Change(int l,int n)
{//now位于las的左下方 左上方 被包含 都是可能的
    int t;
    t=std::min(q[l].x1-1,q[n].x2), Add(q[n].x1,t,q[n].y1,q[n].y2);
    t=std::min(q[l].y1-1,q[n].y2), Add(q[n].x1,q[n].x2,q[n].y1,t);
    t=std::max(q[l].x2+1,q[n].x1), Add(t,q[n].x2,q[n].y1,q[n].y2);
    t=std::max(q[l].y2+1,q[n].y1), Add(q[n].x1,q[n].x2,t,q[n].y2);

    t=std::min(q[l].x2,q[n].x1-1), Subd(q[l].x1,t,q[l].y1,q[l].y2);
    t=std::min(q[l].y2,q[n].y1-1), Subd(q[l].x1,q[l].x2,q[l].y1,t);
    t=std::max(q[l].y1,q[n].y2+1), Subd(q[l].x1,q[l].x2,t,q[l].y2);
    t=std::max(q[l].x1,q[n].x2+1), Subd(t,q[l].x2,q[l].y1,q[l].y2);
}

int main()
{
    n=read(),m=read(); szX=sqrt(n),szY=sqrt(m);
    Discrete();
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            bel[i][j]=(i-1)/szX*szY+(j-1)/szY;
//  for(int i=1; i<=n; ++i,putchar('\n'))
//      for(int j=1; j<=m; ++j) printf("%d ",bel[i][j]);
    Q=read();
    for(int i=1; i<=Q; ++i)
    {
        q[i].x1=read(),q[i].y1=read(),q[i].x2=read(),q[i].y2=read();
        if(q[i].x1>q[i].x2) std::swap(q[i].x1,q[i].x2);//将(x1,y1)设为矩形左下角
        if(q[i].y1>q[i].y2) std::swap(q[i].y1,q[i].y2);
        q[i].id=i;
    }
    std::sort(q+1,q+1+Q);
    Add(q[1].x1,q[1].x2,q[1].y1,q[1].y2);
    Ans[q[1].id]=Now;
    for(int i=2; i<=Q; ++i)
        Change(i-1,i), Ans[q[i].id]=Now;
    for(int i=1; i<=Q; ++i) printf("%lld\n",Ans[i]);

    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/8481776.html

时间: 2024-08-30 09:38:31

BZOJ.2639.矩形计算(二维莫队)的相关文章

【二维莫队】【二维分块】bzoj2639 矩形计算

<法一>二维莫队,对n和m分别分块后,对块从上到下从左到右依次编号,询问以左上角所在块编号为第一关键字,以右下角标号为第二关键字排序,转移时非常厉害. O(q*n*sqrt(n)). #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 201 #define M 100001 struct LiSan{int p,v;}t[N*N]; bool

csp-s模拟测试50(9.22)「施工(单调栈优化DP)」&#183;「蔬菜(二维莫队???)」&#183;「联盟(树上直径)」

改了两天,终于将T1,T3毒瘤题改完了... T1 施工(单调栈优化DP) 考场上只想到了n*hmaxn*hmaxn的DP,用线段树优化一下变成n*hmaxn*log但显然不是正解 正解是很**的单调栈 可以想象到最优情况一定是将两端高于中间的一段平原填成一段平的坑,不然如果坑内存在高度差那么我们即使只将一部分抬升也肯定没有用处,并且如果中间的坑已经高于了两端,再向上升也肯定不优,然后就中间的坑可以很很小,也可以很长,对于这个模型我们首先想到n^2*h的DP 设当前表示的f[i]表示当前到了i节

二维莫队(离线)

什么是莫队算法 莫队算法 何谓二维莫队 区别与一维莫队,无非就是放在了二维上而已. 适用范围 同一维莫队. 二维莫队的思路 依然是将问题离线,将整张图(设长为$n$宽为$m$),我们分别将长和宽分成根号块,然后将其编号,对于每一组询问,我们将其一个端点按块的大小排序,另一个端点直接按大小排序,可以类比一维莫队. 时间复杂度分析 时间复杂度分析是我自己$YY$的,有可能有错,不要声张,不要消费,私信我就好了. 假设询问次数为$q$,长和宽同阶,所以都设为$n$,设块长为$\sqrt{n}$,那么我

PHP计算二维数组指定元素的和

array_sum(array_column($arr, 'num')); //计算二维数组指定元素的和 $arr = [ [ 'id'=>1, 'num'=>3, ], [ 'id'=>2, 'num'=>4, ], [ 'id'=>3, 'num'=>1, ], ]; //计算二维数组指定元素的和 $arr = array_sum(array_column($arr, 'num'));//输出8 var_dump($arr); 原文地址:https://www.cn

bzoj 2038 小z的袜子 莫队例题

莫队,利用可以快速地通过一个问题的答案得到另一问题的答案这一特性,合理地组织问题的求解顺序,将已解决的问题帮助解决当前问题,来优化时间复杂度. 典型用法:处理静态(无修改)离线区间查询问题. 线段树也是处理区间问题的一个有力工具,它和莫队算法各有特点: 线段树可以支持修改,并且单次操作时间复杂度一般为O(log),支持在线,但是要求可以进行快速的区间合并操作,两个区间如不能快速合并(f(n)*O(log)>O(n)),则用线段树就没有什么实际价值了(暴力都比它块) 莫队算法可以解决某些线段树不能

BZOJ 3289 Mato的文件管理(莫队+离散化求逆序数)

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

BZOJ 3289 Mato的文件管理(莫队+树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3289 [题目大意] 求静态区间逆序对. [题解] 我们对查询进行莫队操作,对于区间的删改我们可以计算出改变量对于逆序对的贡献, 利用树状数组维护即可. [代码] #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> const int N=50100;

BZOJ 2038 小z的袜子 &amp; 莫队算法(不就是个暴力么..)

题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概

BZOJ 1452: [JSOI2009]Count 二维树状数组

1452: [JSOI2009]Count Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1452 Description Input Output Sample Input Sample Output 1 2 HINT 题意 题解: 裸的二维树状数组 代码: //qscqesze #include <cstdio> #include <cmath&g