zoukankan      html  css  js  c++  java
  • 树状数组扫描线维护矩形区域和 ICPC2019南京站网络赛

    题目链接:https://nanti.jisuanke.com/t/41298

    题意:给你一个n*n的矩阵(n为1e6),矩阵上的数具有一定规律(给定n后每个位置元素值都已确定,是一个内螺旋矩阵)。之后会加点,被加的点其上的值才会被启用,未被启用的点的值为0,先p次询问,每次询问一个矩形区域内值的和为多少。

    分析:螺旋矩阵找规律可以分成一圈圈的来看,一个n*n的矩阵,那么最多就有n/2个圈,我们先预处理出前i个圈所有的点的数目d[i],之后对每个当前圈,都分为上下左右四侧来找即可。

    该题便转化为一个求矩形区域和的问题,因为n太大了,二维前缀和明显不可以。

    不仅如此,因为n特别大,我们还必须对其进行离散化。

    我们可以把每次询问拆成四次询问,对于查询x1,y1,x2,y2,有

    ans = map[x2][y2]− map[x2][y1−1]− map[x1−1][y2]+ map[x1−1][y1−1]

    考虑二维降低位,将其按x轴排序
    x坐标排序后,我们进行前缀和操作的时候就不需要考虑x坐标了(因为排序后一定有后面来的点的x坐标一定比前面所有的x坐标都大,故当这个点为询问时只需要判断比该点的yy坐标小的点有多少个即可。),然后同时将y坐标离散化掉

    另外,有一个非常相似的题:洛谷P2163 不过这个题数据范围更大,还这样写会T,就分别存储横纵坐标,很值得一看,讲解在这份答案后面。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1000010;
    int T;
    int n,m,p;
    ll d[N],c[N];
    struct node{
        int x1,y1,x2,y2;
    };
    vector<node> qu;//查询
    struct Node{
        int x,y;
        int type;//type为0表示添加新点
    }a[N];
    bool cmp(Node a,Node b){
        if(a.x == b.x){
            if(a.y == b.y)return a.type == 0;
            return a.y < b.y;
        }
        return a.x < b.x;
    }
    //vector<int> v;//离散y
    int b[N];
    int num;
    void add(int x,int y){ for(;x<=num;x+=x&-x)c[x] += y; } ll ask(int x){ ll res = 0; for(;x;x-=x&-x)res += c[x]; return res; } map<int,map<int,int>> mp; int id[N]; ll get(int x,int y){ int cx = n / 2 + 1; int cy = n / 2 + 1;//(cx,cy)即中心坐标 int k = max(abs(x-cx),abs(y-cy));//k表示在第几圈 if(k == 0)return 1ll * n * n; ll res = d[k-1];//圈内有多少个 //分4个case计算 if(y-cy == k && x < cx + k){//在上层 res += cx + k - x; }else if(cx - x == k && y < cy + k){//在左侧 res += k * 2 + cy + k - y; }else if(cy - y == k && x > cx - k){//在下层 res += k * 4 + x - (cx - k); }else if(x - cx == k){//在右侧 res += k * 6 + y - (cy - k); } res = 1ll * n * n - res;//最后倒过来,因为上面是按照中心为1算的 return res + 1; } int calc(ll x){ int res= 0; while(x){res += x % 10;x /= 10;} return res; } int main(){ scanf("%d",&T); d[0] = 1;d[1] = 8; for(int i=2;i<N/2;i++)d[i] = d[i-1] + 8; for(int i=1;i<N/2;i++)d[i] += d[i-1];//计算i圈以内有多少个点 while(T--){ mp.clear();qu.clear();//v.clear(); memset(c,0,sizeof c); scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); a[i] = {x,y,0}; } for(int i=1;i<=p;i++){ int x1,x2,y1,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); qu.push_back({x1,y1,x2,y2}); a[++m] = {x1-1,y1-1,1}; a[++m] = {x2,y2,1}; a[++m] = {x1-1,y2,1}; a[++m] = {x2,y1-1,1}; } //此时m的数量是所有要加的点和要查询的点的总和 for(int i=1;i<=m;i++){ b[i]=a[i].y; } sort(b+1,b+1+m); //v.erase(unique(v.begin(),v.end()),v.end()); num=unique(b+1,b+1+m)-b-1; sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++){ id[i] = lower_bound(b+1,b+1+num,a[i].y) - b; } for(int i=1;i<=m;i++){ if(a[i].type == 0)add(id[i],calc(get(a[i].x,a[i].y))); else if(a[i].type == 1)mp[a[i].x][a[i].y] = ask(id[i]); } for(int i=0;i<p;i++){ int x1 = qu[i].x1,x2 = qu[i].x2,y1 = qu[i].y1,y2 = qu[i].y2; ll res = mp[x2][y2] + mp[x1-1][y1-1] - mp[x1-1][y2] - mp[x2][y1-1]; printf("%lld ",res); } } return 0; }

     接下来是洛谷P2163这道题

    题意是花园里每个数可以用一个整数坐标来表示,多次询问,每次询问一个矩形区域里有多少个树

    离散化这方面我们不再是全部都一起离散化了,而是横纵坐标分别自己离散化,这样可以减少最后的N

    然后我们用了两个存储查询的向量数组Q和q,存储的都是当前询问的下标,并且向量下标分别是左下角的横坐标和右上角的横坐标

    我们知道是按横坐标排序的,我们要求的答案就是F(x2,y2)−F(x1−1,y2)−F(x2,y1−1)+F(x1−1,y1−1)

    之后加减什么的自己体会吧

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int MAXN=500004;
    struct node{int x,y,xx,yy;}a[MAXN];vector<int>V[MAXN],Q[MAXN],q[MAXN];
    int n,m,N,M,sum[MAXN],x[MAXN],y[MAXN],tmpx[MAXN*3],tmpy[MAXN*3],totx,toty,ans[MAXN];
    void modify(int x){for(;x<=M;x+=x&-x)sum[x]++;}
    int query(int x){int ans=0;for(;x;x-=x&-x) ans+=sum[x];return ans;}
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),tmpx[++totx]=x[i],tmpy[++toty]=y[i];
        for(int i=1;i<=m;i++) {
            scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].xx,&a[i].yy);a[i].x--;a[i].y--;
            tmpx[++totx]=a[i].x;tmpx[++totx]=a[i].xx;tmpy[++toty]=a[i].y;tmpy[++toty]=a[i].yy;
        }
        sort(tmpx+1,tmpx+totx+1);sort(tmpy+1,tmpy+toty+1);
        N=unique(tmpx+1,tmpx+totx+1)-tmpx-1;M=unique(tmpy+1,tmpy+toty+1)-tmpy-1;
        for(int i=1;i<=n;i++){
            x[i]=lower_bound(tmpx+1,tmpx+N+1,x[i])-tmpx;
            y[i]=lower_bound(tmpy+1,tmpy+M+1,y[i])-tmpy;
            V[x[i]].push_back(y[i]);
        }
        for(int i=1;i<=m;i++){
            a[i].x=lower_bound(tmpx+1,tmpx+N+1,a[i].x)-tmpx;
            a[i].y=lower_bound(tmpy+1,tmpy+M+1,a[i].y)-tmpy;
            a[i].xx=lower_bound(tmpx+1,tmpx+N+1,a[i].xx)-tmpx;
            a[i].yy=lower_bound(tmpy+1,tmpy+M+1,a[i].yy)-tmpy;
            Q[a[i].x].push_back(i);q[a[i].xx].push_back(i);
        }
        for(int i=1;i<=N;i++){
            for(int j=0;j<V[i].size();j++) modify(V[i][j]);//添加的是纵坐标 
            //接下来两个答案的添加便是这个算法的核心了
            //我们是把一个询问拆成了四个询问 
            for(int j=0;j<Q[i].size();j++) ans[Q[i][j]]+=query(a[Q[i][j]].y)-query(a[Q[i][j]].yy);
            for(int j=0;j<q[i].size();j++) ans[q[i][j]]+=query(a[q[i][j]].yy)-query(a[q[i][j]].y);
        }
        for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
    }
  • 相关阅读:
    镜像转换:img转换为iso
    IDM的谷歌插件安装
    Adams和UG许可证冲突问题
    步进电机驱动总结
    比赛官网收集
    树莓派安装pip包管理工具
    光固化打印后处理过程
    Arduino 串口库函数
    六足蜘蛛机器人行走控制
    温湿度传感器DHT11程序示例
  • 原文地址:https://www.cnblogs.com/qingjiuling/p/11448537.html
Copyright © 2011-2022 走看看