2019ICPC南京网络赛A The beautiful values of the palace

题意:蛇形填数超大版本,需要求出一些给定坐标的值的数位和,然后q次询问,一个矩形区域内值的和是多少

解题思路:二维偏序前缀和的经典题

二维偏序:求(x,y)左下角点的个数,思路是对x,y升序排序,用树状数组维护每个纵坐标y已经出现的次数,这样我们动态地将点的纵坐标y加入树状数组,然后求出比y小的有多少个(树状数组求和)即可知道当前点的二维偏序值了

要求前缀和,我们只需把维护点的个数改为点权即可。

这个题,首先按照蛇形填数的规律求出点权,然后就是求二维偏序前缀和了,有了前缀和我们将可以快速得到子矩阵区域的和了(联想矩阵O(1)查询子矩阵的方法),首先我们要将询问离线,然后把涉及到的点全部存入数组后排序,存储的时候可以采取一些小技巧标记出带权值的点和要求的矩形边界的点,当处理到带权点时,我们加入树桩数组,处理到矩形边界点时,查询一次前缀和,然后计入ans[id],id为询问的时间点,这个题的纵坐标y在1e6以内,因此无需离散化,直接开1e6的树状数组是最佳选择,题目具体细节见代码与注释

AC(补题)代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
struct node
{
    int x,y,val,id;
    friend bool operator<(const node &a,const node &b)
    {
        if(a.x!=b.x)return a.x<b.x;
        if(a.y!=b.y)return a.y<b.y;
        return a.id<b.id;//保证先加入要求和的点值,再加入矩形的边界点,边界点有询问顺序的编号而带值点id=0
    }
}p[maxn];
int n;
int getval(int x,int y)
{
    x=n+1-x;
    y=n+1-y;
    ll ans;
    ll mi=min(x,min(y,min(n-x+1,n-y+1)));
    if(x<=y) ans=1ll*mi*(1ll*4*(n-1)-4*mi)+1ll*10*mi-4*n-3+x+y;
    else ans=1ll*mi*(4*n-4*mi)+1ll*2*mi+1-x-y;//模拟过程
    int tot=0;
    while (ans)
    {
        tot+=ans%10;
        ans/=10;
    }
    return tot;
}
int tree[maxn];//1e6无需离散化
inline int lowbit(int x){ return x&-x;}
void add(int pos,int val)
{
    while (pos<=n)
    {
        tree[pos]+=val;
        pos+=lowbit(pos);
    }
}
ll query(int pos)
{
    ll res=0;
    while (pos>0)
    {
        res+=tree[pos];
        pos-=lowbit(pos);
    }
    return res;
}
ll ans[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T,m,q,x1,y1,x2,y2;
    cin>>T;
    while (T--)
    {
        cin>>n>>m>>q;
        memset(tree,0, sizeof(tree));
        for(int i=1;i<=m;i++)
        {
            cin>>p[i].x>>p[i].y;
            p[i].val=getval(p[i].x,p[i].y);
            p[i].id=0;
        }
        int len=m;
        for(int i=1;i<=q;i++)
        {
            ans[i]=0;
            cin>>x1>>y1>>x2>>y2;
            p[++len].x=x1-1;p[len].y=y1-1;p[len].id=i;p[len].val=1;
            p[++len].x=x2;p[len].y=y2;p[len].id=i;p[len].val=1;
            p[++len].x=x1-1;p[len].y=y2;p[len].id=i;p[len].val=-1;
            p[++len].x=x2;p[len].y=y1-1;p[len].id=i;p[len].val=-1;
        }
        sort(p+1,p+len+1);

        for(int i=1;i<=len;i++)
        {
            if(p[i].id)//如果是矩形边界点则计算答案
            {
                ans[p[i].id]+=1ll*query(p[i].y)*p[i].val;//矩形边界点val的意义是标记ans[id]是加上当前前缀和还是减去当前前缀和
            }
            else add(p[i].y,p[i].val);
        }
        for(int i=1;i<=q;i++)cout<<ans[i]<<'\n';
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xusirui/p/11448642.html