【JZOJ5270】【GDOI2018模拟】神奇的矩阵(二维线段树)

Description

这里写图片描述

Solution

这题直接三方log只有70分,想要打的更好只能打平方log方的,那么很显然就是用一个二维的数据结构来维护。
这还是我第一次打二维线段树(不是线段树套线段树)
首先我们对于绝对值可以考虑小的数被贡献多少次,那么就是找大的数的和-小的数的出现次数,那么我们就可以考虑把所有的数从小到大排序然后依次插入。
然后每个点上统计一个以它为左上角的矩阵可以被贡献多少次,那么每次插进一个数,就把它左上角的矩阵全部都加上a[i],然后询问的时候也是询问左上角的矩阵,左上角的矩阵。(注意要先询问再修改),这样意义其实就是以一个点为左上角的矩阵包括的数集一一与刚插进来的数匹配。
用线段树修改和询问的区间都是同一个区间,那么可以打在一起。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
using namespace std;
const ll maxn=507,mo=1e4+7;
ll i,j,k,l,n,m,ans,tot,yi,er;
ll a[maxn][maxn];
struct nod{
    ll x,y,z;
}b[maxn*maxn];
struct node{
    ll sum,num,bz1,bz2;
}t[maxn*maxn*80];
bool cmp(nod x,nod y){return x.z<y.z;}
void down(ll x,ll l,ll r,ll l1,ll r1){
    ll mid=(l+r)/2;
    if(t[x].bz1){
        t[x*2].sum+=t[x].bz1*(r1-l1+1)*(mid-l+1)%mo;t[x*2+1].sum+=t[x].bz1*(r1-l1+1)*(r-mid)%mo;
        t[x*2].num+=t[x].bz2*(r1-l1+1)*(mid-l+1);t[x*2+1].num+=t[x].bz2*(r1-l1+1)*(r-mid);
        t[x*2].bz1+=t[x].bz1;t[x*2+1].bz1+=t[x].bz1;
        t[x*2].bz2+=t[x].bz2;t[x*2+1].bz2+=t[x].bz2;
        t[x].bz1=t[x].bz2=0;
    }
}
void change(ll x,ll l,ll r,ll l1,ll r1,ll y,ll z,ll yy,ll zz,ll o){
    if(l>=y&&r<=z&&l1>=yy&&r1<=zz){
        yi+=t[x].sum;er+=t[x].num;
        t[x].sum+=(r1-l1+1)*(r-l+1)*o%mo;t[x].num+=(r1-l1+1)*(r-l+1);
        t[x].bz1+=o,t[x].bz2++;
        return;
    }
    down(x,l,r,l1,r1);
    ll mid=(l+r)/2;
    if(z<=mid)change(x*2,l1,r1,l,mid,yy,zz,y,z,o);
    else if(y>mid)change(x*2+1,l1,r1,mid+1,r,yy,zz,y,z,o);
    else change(x*2,l1,r1,l,mid,yy,zz,y,mid,o),change(x*2+1,l1,r1,mid+1,r,yy,zz,mid+1,z,o);
    t[x].sum=t[x*2].sum+t[x*2+1].sum;
    t[x].num=t[x*2].num+t[x*2+1].num;
}
int main(){
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    scanf("%lld%lld%lld",&n,&m,&k);
    fo(i,1,n)fo(j,1,m)scanf("%lld",&a[i][j]),b[++tot].x=i,b[tot].y=j,b[tot].z=a[i][j];
    sort(b+1,b+tot+1,cmp);
    fo(l,1,tot){
        yi=er=0;
        change(1,1,n,1,m,max(b[l].x-k+1,(ll)1),min(n-k+1,b[l].x),max(b[l].y-k+1,(ll)1),min(m-k+1,b[l].y),b[l].z);
     //   b[l].z%=mo;
        ans=ans+er*b[l].z%mo-yi;
    }
    ans=(ans*2%mo+mo)%mo;
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值