2019南京网路赛 A.The beautiful values of the palace (主席树)

传送门

题意

给一个 \(n\times n\) 的矩阵,以螺旋线的形式给每个位置的预设值,有 \(m\) 个位置的值为本身的预设值,其余位置为 \(0\),然后 \(p\) 组询问,每组询问回答子矩阵中的总值为多少。

题解

有不少题解都是用离线树状数组的方法解决的,但是如果学习了主席树过后,可以发现主席树是解决这种二维偏序问题的利器。
首先对于 \(m\) 个有值得点,\(O(1)\) 地求出它得价值,不会得可以去学习一下螺旋矩阵
因为坐标在 \(2\cdot 10^6\) 范围里,对于主席树来说有点大了,所以把 \(y\) 坐标离散化,这样主席树开 \(mlog_2m\) 个点就好。
然后把每个点以 \(x\) 坐标从小到大排序之后依次更改主席树,这颗主席树相当于维护了 \(m\) 个线段树,每个线段树统计已加入的点 \(y\) 坐标在区间 \([l,r]\) 中的所有点的总价值是多少。
对于每个询问,看 \([x1,x2]\) 这个区间里的信息是由哪两颗线段树的差值形成的,然后查询这两颗线段树在区间 \([y1,y2]\) 中的差值总和,就是答案了,当然由于离散化,所以 \(y1,y2\) 一定要以离散化后的区间为准。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=1e5+10;
int n,m,p,root[MAXN];
int b[MAXN],cnt;
struct Point{
    int x,y;
    LL v;
    void getv(){
        int mid=(n+1)>>1;
        int tx=x-mid,ty=y-mid;
        LL c=max(abs(tx),abs(ty));
        LL id=c*(c-1)*8/2;
        if(ty==c&&-c<=tx&&tx<c) id+=c-tx;
        else if(tx==-c&&-c<=ty&&ty<c) id+=c*2+c-ty;
        else if(ty==-c&&-c<tx&&tx<=c) id+=2*c*2+c+tx;
        else id+=3*c*2+c+ty;
        LL temp=1ll*n*n-id;
        v=0;
        while(temp) v+=temp%10,temp/=10;
    }
    friend bool operator < (const Point& a,const Point& b){
        return a.x<b.x;
    }
}pt[MAXN];

struct HjtTree{
    #define mid ((l+r)>>1)
    LL sum[MAXN*40];
    int ls[MAXN*40],rs[MAXN*40],tot;
    void build(int& id,int l,int r){
        id=++tot;sum[id]=0;
        if(l==r) return;
        build(ls[id],l,mid);
        build(rs[id],mid+1,r);
    }
    void update(int pre,int& id,int l,int r,int pos,LL x){
        id=++tot;sum[id]=sum[pre]+x;ls[id]=ls[pre];rs[id]=rs[pre];
        if(l==r) return;
        if(pos<=mid) update(ls[pre],ls[id],l,mid,pos,x);
        else update(rs[pre],rs[id],mid+1,r,pos,x);
    }
    LL ask(int u,int v,int l,int r,int L,int R){
        if(L<=l&&r<=R) return sum[v]-sum[u];
        LL res=0;
        if(L<=mid) res+=ask(ls[u],ls[v],l,mid,L,R);
        if(R>mid) res+=ask(rs[u],rs[v],mid+1,r,L,R);
        return res;
    }
    #undef mid
}tree;

void solve(){
    scanf("%d%d%d",&n,&m,&p);
    cnt=0;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&pt[i].x,&pt[i].y);pt[i].getv();
        b[++cnt]=pt[i].y;
    }
    sort(b+1,b+cnt+1);cnt=unique(b+1,b+cnt+1)-b-1;
    for(int i=1;i<=m;i++) pt[i].y=lower_bound(b+1,b+cnt+1,pt[i].y)-b;
    sort(pt+1,pt+m+1);
    tree.tot=0;
    tree.build(root[0],1,cnt);
    for(int i=1;i<=m;i++) tree.update(root[i-1],root[i],1,cnt,pt[i].y,pt[i].v);
    for(int i=1,x1,y1,x2,y2;i<=p;i++){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int u=lower_bound(pt+1,pt+m+1,(Point){x1,0,0})-pt-1;
        int v=upper_bound(pt+1,pt+m+1,(Point){x2,0,0})-pt-1;
        int l=lower_bound(b+1,b+cnt+1,y1)-b;
        int r=upper_bound(b+1,b+cnt+1,y2)-b-1;
        if(u>v||l>r) printf("0\n");
        else printf("%lld\n",tree.ask(root[u],root[v],1,cnt,l,r));
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
#endif
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/BakaCirno/p/12307078.html