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

Description

【JZOJ5270】【GDOI2018模拟】神奇的矩阵(二维线段树)_第1张图片

Solution

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

Code

#include
#include
#include
#include
#include
#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);
}

你可能感兴趣的:(省选,线段树)