牛客寒假算法基础集训营6 E:海啸(二维树状数组 or 前缀和 +容斥定理)

【题目】

海啸

【题解】

法一:

树状数组(单点更新区间查询)板子题,就是1≤n×m≤10^6这个数据范围让我懵了下,用vector容器就好了。

如果大于等于d就等于单点更新1,看做是二维区间查询和即可。

法二:

啧啧,不好意思给我写麻烦了,其实做两次前缀和即可。(比如a[2][2]里存的就是a[1][1]+a[1][2]+a[2][1]+a[2][2]的值。)

区间查询用到了容斥定理哦。

【法一】

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define Pi acos(-1)
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
#define lowbit(x) x&(-x)
vector <ll> ve[maxn];
vector <ll> vec[maxn];
ll n,m;
void add(ll x,ll y,ll val)
{
    while(x<=n)
    {
        for(ll i=y;i<=m;i+=lowbit(i))
        {
            vec[x][i]+=val;
        }
        x+=lowbit(x);
    }
}
ll sum(ll x,ll y)
{
    ll res=0;
    while(x>0)
    {
        for(ll i=y;i>0;i-=lowbit(i))
            res+=vec[x][i];
        x-=lowbit(x);
    }
    return res;
}
ll query(ll x1,ll y1,ll x2,ll y2)
{
    return sum(x2,y2)+sum(x1-1,y1-1)-sum(x2,y1-1)-sum(x1-1,y2);
}
int main()
{
    ll d; scanf("%lld%lld%lld",&n,&m,&d);
    ll xx,q;
    for(ll i=1;i<=n;i++)
    {
        ve[i].push_back(0),vec[i].push_back(0);
        for(ll j=1;j<=m;j++)
        {
            scanf("%lld",&xx);
            ve[i].push_back(xx);
            vec[i].push_back(0);
        }
    }
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=m;j++)
        {
            //printf("%lld\n",ve[i][j]);
            if(ve[i][j]>=d) add(i,j,1);
        }
    scanf("%lld\n",&q);
    ll a,b,x,y;
    while(q--)
    {
        scanf("%lld%lld%lld%lld",&a,&b,&x,&y);
        printf("%lld\n",query(a,b,x,y));
    }
    return 0;
}

【法二】

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define Pi acos(-1)
#define eps 1e-8
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
vector <ll> vec[maxn];
ll n,m;
ll query(ll x1,ll y1,ll x2,ll y2)
{
    return vec[x2][y2]+vec[x1-1][y1-1]-vec[x2][y1-1]-vec[x1-1][y2];
}
int main()
{
    ll d; scanf("%lld%lld%lld",&n,&m,&d);
    ll xx,q;
    for(ll j=0;j<=m;j++) vec[0].push_back(0);
    for(ll i=1;i<=n;i++)
    {
        vec[i].push_back(0);
        for(ll j=1;j<=m;j++)
        {
            scanf("%lld",&xx);
            if(xx>=d) xx=1;
            else xx=0;
            xx+=vec[i][j-1];
            vec[i].push_back(xx);
        }
    }
    for(ll i=2;i<=n;i++)
        for(ll j=1;j<=m;j++)
            vec[i][j]+=vec[i-1][j];
    scanf("%lld\n",&q);
    ll a,b,x,y;
    while(q--)
    {
        scanf("%lld%lld%lld%lld",&a,&b,&x,&y);
        printf("%lld\n",query(a,b,x,y));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值