首页 > 其他分享 >P4148 简单题 题解

P4148 简单题 题解

时间:2024-01-20 19:11:42浏览次数:28  
标签:siz ch return int 题解 top mid P4148 简单

Question

P4148 简单题

有一个 \(n\times n\) 的棋盘,每个格子内有一个整数,初始时全部为 \(0\),现在需要维护两种操作

  • 1 x y 将格子 \(x,y\) 里的数字加上 \(A\)
  • 2 x1 y1 x2 y2 输出 \(x_1,y_1,x_2,y_2\) 这个矩形内的数字和

强制在线

Solution

因为强制在线,没法用 CDQ 什么乱搞,这是一道 K-D 树的板子题

维护每颗子树中所有点 \(x\) 和 \(y\) 坐标的最大和最小值,也就是这个子树包括小矩形的范围,如果当前子树在查询区间内,就直接返回这颗子树的权值和,否则就判断根节点是否在查询子树内,然后递归处理左右儿子

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
const double alpha = 0.75;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct Point{
    int dim[2],val;
    Point(){};
    Point(int x,int y,int v):dim{x,y},val(v){}
};

Point order[maxn]; int cnt;

struct node{
    int ls,rs;
    int mi[2],ma[2];
    int sum;
    int siz;
    Point p;
}t[maxn];

int tot,rt;
int top,stk[maxn];

int now;
bool cmp(const Point &a,const Point &b){
    return a.dim[now] < b.dim[now];
}

void update(int u){
    for(int i=0;i<2;i++){
        t[u].mi[i] = t[u].ma[i] = t[u].p.dim[i];
        if(t[u].ls){
            t[u].mi[i] = min(t[u].mi[i],t[t[u].ls].mi[i]);
            t[u].ma[i] = max(t[u].ma[i],t[t[u].ls].ma[i]);
        }
        if(t[u].rs){
            t[u].mi[i] = min(t[u].mi[i],t[t[u].rs].mi[i]);
            t[u].ma[i] = max(t[u].ma[i],t[t[u].rs].ma[i]);
        }
    }
    t[u].sum = t[t[u].ls].sum + t[t[u].rs].sum + t[u].p.val;
    t[u].siz = t[t[u].ls].siz + t[t[u].rs].siz + 1;
}

void slap(int u){
    if(!u) return ;
    slap(t[u].ls);
    order[++cnt] = t[u].p;
    stk[++top] = u; //回收节点
    slap(t[u].rs);
}

int build(int l,int r,int d){
    if(l > r) return 0;
    int u;
    if(top) u = stk[top--];
    else u = ++tot;
    int mid = (l+r)>>1;
    now = d;
    nth_element(order+l,order+mid,order+r+1,cmp);
    t[u].p = order[mid];
    t[u].ls = build(l,mid-1,d^1); t[u].rs = build(mid+1,r,d^1);
    update(u);
    return u;
}

bool not_balance(int u){
    if(t[t[u].ls].siz > t[u].siz*alpha || t[t[u].rs].siz > t[u].siz*alpha) return true;
    return false;
}

void insert(int &u,Point p,int d){
    if(!u){
        if (top) u = stk[top--];
        else u = ++tot;
        t[u].ls = t[u].rs = 0; t[u].p = p;
        update(u);
        return ;
    }
    if(p.dim[d] <= t[u].p.dim[d]) 
        insert(t[u].ls,p,d^1);
    else 
        insert(t[u].rs,p,d^1);
    update(u);
    if(not_balance(u)){
        cnt = 0;
        slap(u);
        u = build(1,cnt,d); //重建
    }
}

int query(int u, int x1, int y1, int x2,int y2){  //询问 x1,y1,x2,y1 这个矩形里面的和
    if(!u) return 0;
    int X1 = t[u].mi[0], Y1 = t[u].mi[1], X2 = t[u].ma[0], Y2 = t[u].ma[1]; //[X1,Y1]为矩形左下角,[X2,Y2]为矩形右上角
    if(x1 <= X1 && X2 <= x2 && y1 <= Y1 && Y2 <= y2) return t[u].sum;  //子树表示的矩形完全在查询矩形内
    if(x1 > X2 || x2 < X1 || y1 > Y2 || y2 < Y1) return 0; //子树表示的矩形完全在查询矩形外
    int ans = 0;
    if(x1 <= t[u].p.dim[0] && t[u].p.dim[0] <= x2 && y1 <= t[u].p.dim[1] && t[u].p.dim[1] <= y2)
         ans += t[u].p.val; //子树的根在查询矩形内
    ans += query(t[u].ls,x1,y1,x2,y2) + query(t[u].rs,x1,y1,x2,y2); //递归查询左右子树
    return ans;
}

int main(){
    freopen("P4148.in","r",stdin);
    int n = read();
    int ans=0;
    while(1){
        int op = read();
        if(op == 1){
            int x = read(), y = read(), val = read();
            x ^= ans; y ^= ans; val ^= ans;
            insert(rt,Point(x,y,val),0);
        }
        if(op == 2){
            int x1 = read(), y1 = read(), x2 = read(), y2 = read();
            x1 ^= ans; y1 ^= ans; x2 ^= ans; y2 ^= ans;
            ans = query(rt,x1,y1,x2,y2);
            printf("%d\n",ans);
        }
        if(op == 3) break;
    }
    return 0;
}

标签:siz,ch,return,int,题解,top,mid,P4148,简单
From: https://www.cnblogs.com/martian148/p/17976996

相关文章

  • P4747 [CERC2017] Intrinsic Interval 题解
    题目链接:IntrinsicInterval讲讲析合树如何解决这种问题,其实这题很接近析合树的板题的应用。增量法进行析合树建树时,需要用ST表预处理出\(max\)和\(min\)以便\(O(1)\)查询极差,然后线段树去维护\([l,r]\)上的极差减区间端点做差即\(diff-(r-l)\),当这玩意等于\(0\)时......
  • P8112 [Cnoi2021] 符文破译 题解
    题目传送门思路先看数据范围,我们发现两个字符串的长度最大会达到\(5\times10^7\)。这立刻打消了我用暴力的想法。于是,我选择了用KMP模式匹配,这一个能够在线性时间内判定字符串\(A\)是否是字符串\(B\)的字串,并求出字符串\(A\)在字符串\(B\)中各次出现的位置。如......
  • HDU2966 In case of failure 题解
    QuestionHDU2966Incaseoffailure给出平面上\(n\)个点坐标,求每个点最近的点的欧几里得距离的平方Solution算是一道K-D树的板子题维度\(K=2\)建立\(K-D\)树,在每一层更新当前最小答案\(now\_ans\),如果在然后继续遍历当前维度下距离\(\le\)的区块随机数据时间复......
  • 题解 P6226 [BalticOI 2019 Day1] 潜艇
    【洛谷博客】题解P6226[BalticOI2019Day1]潜艇题意很清楚,忽略。分析看到这种字符串题很容易想到直接广度优先搜索,复杂度\(O(rc4^m)\)。很显然承受不了,所以考虑DP。状态设计设\(f_{i,x,y}\)表示执行完前\(i\)个操作后位置\((x,y)\)能否作为终点。设命令字符......
  • 题解 [ABC321D] Set Menu
    【洛谷博客】题意给定一个长度为\(N\)的正整数数列\(A\),和一个长度为\(M\)的正整数数列\(B\),还有一个正整数\(P\)。你需要求:\[\sum\limits^{N}_{i=1}\sum\limits^{M}_{j=1}\min(A_i+B_j,P)\]分析说实话感觉这题比C还要简单。先考虑单个\(A_i\)能产生的贡献,可以......
  • 题解 [ABC321C] 321-like Searcher
    【洛谷博客】题意给定一个\(k\),你需要找到第\(k\)小的满足下面条件的正整数:对于这个数的每一位,高位大于低位。分析这个数据范围仅有一个\(1\lek\),让人很不好下手。我们不妨先做一个DP,看有多少个满足这样条件的数。设\(f_{i,j}\)表示有\(i\)位,且最高位为\(j\)......
  • 题解 [ABC242D] ABC Transform
    【洛谷博客】很巧妙的一道题。题意给定一个字符串\(S\),只包含字符A,B,C。每过一个时刻,字符会发生变化:A\(\to\)BC,B\(\to\)CA,C\(\to\)AB。设\(0\)时刻为\(S_0=S\)。进行\(Q\)次询问,每次询问时刻\(t\)时,字符串\(S_t\)中第\(k\)个字符。分析为了方便处理,我这里将所......
  • 题解 [ABC144E] Gluttony
    【洛谷博客】题意翻译很清楚,略。分析经过观察最优方案一定是消化代价小的配难消化的菜。所以将\(F\)从小到大排序,\(A\)从大到小排序,当然也可以反着来。因为有\(K\)次修行的机会,难以直接贪心。因为随着时间增加,修行的使用次数会减少,存在单调性。所以考虑使用二分答案转......
  • 题解 [ABC186F] Rook on Grid
    【洛谷博客】有一点难度,但不多。题意一个\(H\timesW\)的地图上有\(M\)个障碍物。有一辆车在\((1,1)\),一次行动可以向下或向右移动任意格(不得穿过障碍物)。求这辆车在最多两次行动中可能到达多少个格子。分析车有四种选择:向右、向下、先向右再向下、先向下再向右。然......
  • 题解 [ABC186E] Throne
    【洛谷博客】同余方程板子题,没过的可以先去看看。题意翻译给的很清楚。分析看到这个转圈圈的就很容易想到同余方程。为了方便处理,我们就将编号全部减\(1\),于是编号就变成\(0\simN-1\)。然后就可以很容易的列出同余方程:\[S+Kx\equiv0\pmod{N}\]移项可得:\[Kx\equ......