2019 南京 网络赛 B (二维偏序,树状数组离线)

题意:

       给出一N*N的蛇形矩阵,具体位置元素值不给你,自己找规律,然后给你M个

有效位置,P次查询,每次查询一个子矩阵中有效元素的权值和,该权值和等于对于

每个有效元素,模10拆分后相加得到的和。(注意有效点以及询问x、y所代表的含义,

是x轴,y轴)。

input
1
3 4 4
1 1
2 2
3 3
2 3
1 1 1 1
2 2 3 3
1 1 3 3
1 2 2 3

output

5
18
23
17

思路:

           第一个难点在于回旋矩阵给出x,y坐标让我们求对应值,找规律写通项公式。

           其次查询二维区间和的难点在于10^6*10^6的矩阵无法表示,用map空间上可以,

但是map修改操作带个log,所以会超时,即使是用二维树状数组。不信看下面的超时代码。

           比赛时自己的想法很接近正确思路,离线然后树状数组维护,但是因为这种题目做的

比较少,知识点不够熟练,所以没能写出来

           比赛思考如下,因为有过见过一些类似题目(见过,代码实现的基本没有),所以

很容易往离线查询上想,然后树状数组维护。因为考虑到1e6*1e6的矩阵大小,但有效点最多

只有1e6个,所以想到降维处理,二维变为一维,然后对于每一次查询,有两个限制因素,一个

在x轴上,一个在y轴上,不难想到如果我们将所有有效点按x从小到大排序,那么现在对于

每次询问来说,只需要考虑在L,R(L,R很容易确定)的连续区间内,y值位于限定范围内

的那些离散的元素即可(事实证明这里的L,R没啥实质作用,只是思考的过程产生的想法)。

显然我们的BIT要建立在y的值域上,并按照x轴顺序不断更新BIT,在当前x位置,前x列的元素

都已经被添加,所以我们可以查询任意y区间的区间和,但注意,这里求出的y的区间和是对于

1~x所有列来说的,而并不是我们所需要的 x 范围。(比赛中bug就在这里,没有考虑到去重,

通过样例发现问题后也没有想到合适的解决办法)。

         一个很巧妙的解决方法是每个询问拆成两部分,两者均为1~对应x的前缀统计(差分公式拆分)。

只不过一个为正数,一个为负数,这样每次询问的答案就由两个询问共同组成。

         再仔细考虑离线查询的过程,我们是先枚举有效点,然后移动询问,还是先枚举询问再移动有效点。

自己当时写的代码采取了第一种方案,但自己写的有很大bug,当x1位置被更新后,我们要计算所有

位于x1和x2之间的询问,(而不仅仅是x1相关的询问,比赛时的bug)。*****(所以说理论上两种枚举方

案都可以,不过本题第一种方案麻烦一点,要对最后一个有效位置单独处理)。看了题解大都第二种方案,

直接枚举询问,对于当前询问,把它前面所有的有效位置全部更新,这样就不用特殊处理了,较方便

          引申......上面对于询问的拆分,难道不就是线段树优化扫描线时的方法么,拆分后的项由1个x,2个y

(或1个y,2个x)组成,权值分别为1和-1 ... ...

           有大佬 直接利用二维差分前缀和的公式,将一次查询拆分为四次查询:

ans = map[x2][y2]-map[x2][y1-1]-map[x1-1][y2]+map[x1-1][y1-1] 。

然后直接按照x的顺序依次统计相应y的数量即可。虽然这样的拆分是多余的,因为只要x或者y一

者被拆分,另一者完全可以通过前缀和求解给定L,R的权值和,且无遗漏无重复。(或者说造成:

如按x排序,bit针对y轴,造成y冗余的原因是因为x的冗余,所以对x拆分即可)。但是4项拆分思

维量对目前水平的自己来说看起来好像思维量小好多,关键在于理解好4项拆分为什么是正确的?

代码实现(二维树状数组TLE):

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <map>

#define inf 0x3f3f3f3f
#define LL long long

using namespace std;

const int N = 1e6 + 100;
map<int,long long>bit[N];
int xx[N],yy[N],tot;
void add(int x,int y,long long val,int n) {
    for(int i=x; i<=n; i+=i&-i) {
        for(int j=y; j<=n; j+=j&-j) {
            bit[i][j]+=val;
            xx[++tot]=i;
            yy[tot]=j;
        }
    }
}
long long qu(int x,int y) {
    long long res = 0;
    for(int i=x; i; i-=i&-i)
        for(int j=y; j; j-=j&-j) {
            res+=bit[i][j];
        }
    return res;
}

LL sol(LL n, LL x, LL y) {
    LL qs = n / 2, q = min(n - y + 1, min(n - x + 1, min(x, y))) - 1;
    if (x == qs + 1 && y == qs + 1)	return n * n;
    LL ans = 1ll * q * (8 * qs + 8 * (qs - q + 1)) / 2;
    if (n - x == q)	ans += n - q - y + 1;
    else if (y - 1 == q)ans += n - 2 * q + 1 + n - q - 1 - x;
    else if (x - 1 == q)ans += n - 2 * q + 1 + n - 2 * q - 2 + y - q - 1;
    else ans += n - 2 * q + 1 + n - 2 * q - 2 + n - 2 * q - 1 + x - q - 1;
    return ans;
}
int main() {
#ifdef MYHOME_Wjvje
    freopen("input.txt","r",stdin);
#endif
    int t;
    //cout<<(sizeof(bit))/1024<<endl;
    scanf("%d",&t);
    while(t--) {
        while(tot)bit[xx[tot]][yy[tot]]=0,tot--;
        int n,m,p;
        scanf("%d%d%d",&n,&m,&p);
        int x,y;
	//TLE
        for(int i=1; i<=m; i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y,sol(n,x,y),n);
        }
        //TLE too...
        for(int i=1; i<=p; i++) {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            printf("%lld\n",qu(x2,y2)-qu(x1,y1-1)-qu(x1-1,y1)+qu(x1-1,y1-1));

        }
    }
    return 0;
}

 两项拆分离线AC代码:     

      

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;

const int N = 2e5+100;
const int M = 4e5+100;

struct Node {
    int r,c;
    long long val;
} arr[N];
bool cmp1(Node aa,Node bb) {
    return aa.r<bb.r;
}
bool cmp2(Node aa,Node bb) {
    return aa.c<bb.c;
}
//回旋矩阵O(1)求值
LL sol1(LL n, LL x, LL y) {
    x=x-n/2-1;
    y=y-n/2-1;
    long long tmp=max(abs(x),abs(y)),ans;
    if(x>=y)ans=n*n-4*tmp*tmp-2*tmp-x-y;
    else ans=n*n-4*tmp*tmp+2*tmp+x+y;
    return ans;
}

long long sol2(long long x) {
    long long res=0;
    while(x) {
        res+=x%10;
        x/=10;
    }
    return res;
}

struct query {
    int x1,y1,x2,y2,id;
    int val;
} qrr[N];
bool cmp3(query aa,query bb) {
    return aa.x2<bb.x2;
}

int bst[N],ans[N];
void update(int x,int v,int n) {
    for(; x<=n; x+=x&-x)bst[x]+=v;
}
int qu(int x) {
    int res=0;
    for(; x; x-=x&-x)res+=bst[x];
    return res;
}
int main() {
#ifdef MYHOME_Wjvje
    freopen("input.txt","r",stdin);
#endif
    int t,n,m,p,tot;
    int x,y;
    scanf("%d",&t);
    while(t--) {
        tot=0;

        memset(bst,0,sizeof(bst));
        memset(ans,0,sizeof(ans));
        scanf("%d%d%d",&n,&m,&p);
        for(int i=1; i<=m; i++) {
            scanf("%d%d",&x,&y);
            arr[i].r=x;
            arr[i].c=y;
            arr[i].val=sol2(sol1(n,x,y));
        }
        sort(arr+1,arr+1+m,cmp1);
        for(int i=1; i<=p; i++) {
            scanf("%d%d",&qrr[i].x1,&qrr[i].y1);
            scanf("%d%d",&qrr[i].x2,&qrr[i].y2);
            qrr[i].id=i;
            qrr[i].val=1;

            qrr[i+p].id=i;
            qrr[i+p].val=-1;
            qrr[i+p].y1=qrr[i].y1;//1个 x对 2个 y.
            qrr[i+p].y2=qrr[i].y2;
            qrr[i+p].x2=qrr[i].x1-1;
        }
        sort(qrr+1,qrr+1+p*2,cmp3);
        int pw=1;
        //先枚举查询,另一种枚举理论上可行
        for(int i=1; i<=p*2; i++) {
            while(pw<=m&&arr[pw].r<=qrr[i].x2)
                update(arr[pw].c,arr[pw].val,n),pw++;
            ans[qrr[i].id]+=qrr[i].val*(qu(qrr[i].y2)-qu(qrr[i].y1-1));
        }
        for(int i=1; i<=p; i++)printf("%d\n",ans[i]);
    }
    return 0;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

四项拆分离线AC代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5+100;

struct Node {
    int x,y;
    long long val;
    bool operator <(const Node & obj)const {
        return x<obj.x;
    }
} arr[N];
//回旋矩阵求值
long long sol1(long long n,long long x,long long y) {
    x=x-n/2-1;
    y=y-n/2-1;
    long long tmp=max(abs(x),abs(y)),ans;
    if(x>=y)ans=n*n-4*tmp*tmp-2*tmp-x-y;
    else ans=n*n-4*tmp*tmp+2*tmp+x+y;
    return ans;
}
long long sol2(long long x) {
    long long res=0;
    while(x) {
        res+=x%10;
        x/=10;
    }
    return res;
}

struct query {
    int x,y,id,val;
    bool operator <(const query & obj)const {
        return x<obj.x;
    }
} qrr[4*N];

long long bst[N],ans[N];
void update(int x,int v,int n) {
    for(; x<=n; x+=x&-x)bst[x]+=v;
}
long long qu(int x) {
    long long res=0;
    for(; x; x-=x&-x)res+=bst[x];
    return res;
}
int main() {
#ifdef MYHOME_Wjvje
    freopen("input.txt","r",stdin);
#endif
    int t,n,m,p;
    int x,y;
    scanf("%d",&t);
    while(t--) {
        memset(bst,0,sizeof(bst));
        memset(ans,0,sizeof(ans));
        scanf("%d%d%d",&n,&m,&p);
        for(int i=1; i<=m; i++) {
            scanf("%d%d",&x,&y);
            arr[i].x=x;
            arr[i].y=y;
            arr[i].val=sol2(sol1(n,x,y));
        }
        sort(arr+1,arr+1+m);
        for(int i=1; i<=p; i++) {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            //sum=map[x2][y2]−map[x2][y1−1]−map[x1−1][y2]+map[x1−1][y1−1]
            qrr[i]=query {x2,y2,i,1};
            qrr[i+p]=query {x1-1,y2,i,-1};
            qrr[i+2*p]=query {x2,y1-1,i,-1};
            qrr[i+3*p]=query {x1-1,y1-1,i,1};
        }
        sort(qrr+1,qrr+1+p*4);
        int pw=1;//离线
        for(int i=1; i<=p*4; i++) {
            while(pw<=m&&arr[pw].x<=qrr[i].x)
                update(arr[pw].y,arr[pw].val,n),pw++;
            ans[qrr[i].id]+=qrr[i].val*qu(qrr[i].y);
        }
        for(int i=1; i<=p; i++)printf("%lld\n",ans[i]);
    }
    return 0;
}

THE END;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值