2019 南京网络赛 B The beautiful values of the palace(树状数组)

链接

题意:

在这里插入图片描述
有n*n的矩阵,值从右上开始一圈一圈往里面递增。(1,1)在左下,(n,n)在右上。
给出m个宫殿,每个宫殿有一个美丽值,美丽值为矩阵所在位置上的值的数位和,如18=1 + 8 = 9
给出q个询问,每个询问是一个矩阵,求在矩阵内的宫殿美丽值的和。


思路:

求在矩阵上的值找了个螺旋矩阵的模板。在知道了每个宫殿的美丽值之后,如何快速地解决问题。首先需要离线做,记录所有的询问。询问也是一个矩阵,那么就可以用二维前缀的形式,二维的话空间可能不太行,那么就需要优化成一维。把询问拆点,每个询问变成4个点分别是(x-1,y-1,1),(x-1,y,-1),(x,y-1,-1),(x,y,1),1和-1表示符号,然后把这些拆出来的点和宫殿一起按照x、y排序,这样x是递增的,只需要维护y轴方向的前缀和就好了。


参考代码:
#include <bits/stdc++.h>
#define  _debug(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
const int M = 1e5+5;
struct node2 {
    ll x, y, val, tp, id, fh;//tp(true:询问,false:宫殿),fh(询问拆点的符号)

    node2() {}
    node2(ll x, ll y, ll id, ll fh) {
        this->x = x;
        this->y = y;
        this->tp = 1;
        this->id = id;
        this->fh = fh;
    }

    bool operator<(const node2 &a) const {
        if (x == a.x && y == a.y)return tp < a.tp;//同坐标时宫殿优先
        if (x == a.x)return y < a.y;
        return x < a.x;
    }
} po[M * 5];

ll n, m, q;
ll real_val(ll x) {
    ll ret = 0;
    while (x) {
        ret += x % 10;
        x /= 10;
    }
    return ret;
}

ll get_val(ll x, ll y, ll n){
    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;
}


ll tree[N];
ll ans[M];

int lowbit(int x){
    return x&(-x);
}

void update(int x,int ed,ll val){
    if(x<1)return;
    for(;x<ed;x+=lowbit(x)){
        tree[x]+=val;
    }
}

ll query(int x){
    ll ret=0;
    for(;x>0;x-=lowbit(x)){
        ret+=tree[x];
    }
    return ret;
}

void solve() {
    for (ll i = 1; i <= m; i++) {
        scanf("%lld%lld", &po[i].x, &po[i].y);
        po[i].val = real_val(get_val(po[i].x, po[i].y,n));
        po[i].tp=0;
    }

    ll top = m;
    ll x1, y1, x2, y2;
    for (ll i = 1; i <= q; i++) {//询问拆点
        scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
        po[++top] = node2(x1 - 1, y1 - 1, i, 1);
        po[++top] = node2(x2, y2, i, 1);
        po[++top] = node2(x1 - 1, y2, i, -1);
        po[++top] = node2(x2, y1 - 1, i, -1);
    }

    sort(po + 1, po + 1 + top);
    memset(tree,0, sizeof(tree));
    memset(ans, 0, sizeof(ans));
    for (ll i = 1; i <= top; i++) {
        if (po[i].tp) {
            ans[po[i].id] += po[i].fh * query(po[i].y);
        } else {
            update(po[i].y,N, po[i].val);
        }
    }
    for (ll i = 1; i <= q; i++) {
        printf("%lld\n", ans[i]);
    }
}

int main() {
    ll t;
    scanf("%lld", &t);
    for (ll ca = 1; ca <= t; ca++) {
        scanf("%lld%lld%lld", &n, &m, &q);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值