jzoj5270. 【GDOI2018模拟8.14】神奇的矩阵

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

一行表示答案

Sample Input

3 3 2
1 2 3
4 5 6
7 8 9

Sample Output

112

Data Constraint

在这里插入图片描述

赛时

考试时想了很多很多很多很多……(此处省略很多很多)的方法。
反正最后要不就是少考虑一种情况,要不就是时间复杂度极其不优秀。
最后一个小时全速码力,怒肝 O ( n 3   l o g   n ) O(n^3\ log\ n) O(n3 log n)
结果头脑发热,线段树都打错好几次。
最后获得了0分好成绩。

题解

然鹅时间复杂度是长这样的: O ( ( n − k ) 2 ∗ k   l o g   ( n 2 ) ) O((n-k)^2*k\ log\ (n^2)) O((nk)2k log (n2))
后面的 l o g log log其实20不到,主要是前面的复杂度。
然后求导(不是我求的)可以发现,当k取166时复杂度达到最大为3s,而题目给了5s。

我们发现,只要常数够小就可以水过去。
考虑这样怎么做。

首先,对于一个给定的矩阵怎么求。我们发现,绝对值直接搞不好搞。
然后我们可以弄一个树状数组(离散化),维护两个东东,一个是和,一个是数量。
对于一个数p,直接求出比之小的数的和和个数。然后用总和减去即可。

那么我们先 O ( k 2   l o g   n 2 ) O(k^2\ log\ n^2) O(k2 log n2)地求出最左边的矩阵的树状数组。
然后每次就往右边平移,每次直接树状数组修改把最左边一列给删掉,然后把最右边的一列加入树状数组。

那么这样就可以十分暴力简单地求出答案了。
然鹅每次求最左边的矩阵所耗时似乎有点多,那么我们可以考虑蛇形走位来维护树状数组。
那么往右平移做完第一行时,我们就往下平移一行,然后再向左平移。依次往复。

这样就是稳定的 O ( ( n − k ) 2 ∗ k   l o g   ( n 2 ) ) O((n-k)^2*k\ log\ (n^2)) O((nk)2k log (n2))

似乎有个 O ( n 2   l o g   n ) O(n^2\ log\ n) O(n2 log n)的神奇做法,然鹅我不废。

代码

跑了4s左右。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const long long mo=10007;
const int maxn=501;

int n,m,a[maxn][maxn],b[maxn*maxn],c[maxn][maxn],rot[maxn*maxn],answer,x[maxn*maxn],y[maxn*maxn],cnt,k,gs,up,gg;
long long tree[maxn*maxn*10],jl,ans,sum,p,zh;
int le[maxn*maxn*10],re[maxn*maxn*10],op[maxn*maxn*10];

void qsort(int l,int r)
{
	int i=l;int j=r;
	int m=b[(i+j)/2];
	while (i<=j)
	{
		while (b[i]<m) i++;
		while (b[j]>m) j--;
		if (i<=j)
		{
			swap(x[i],x[j]);
			swap(y[i],y[j]);
			swap(b[i],b[j]);
			i++;j--;
		}
	}
	if (l<j) qsort(l,j);
	if (r>i) qsort(i,r); 
}

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

void modify(int i,long long k,long long g)
{
	while (i<=up)
	{
		tree[i]=(tree[i]+k*g)%mo;
		op[i]=(op[i]+g)%mo;
		i+=lowbit(i);
	}
}

void getans(int i)
{
	int kk=0;
	while (i>0)
	{
		jl=(jl+tree[i])%mo;
		gg=(gg+op[i])%mo;
		kk=lowbit(i);
		i-=kk;
	}
}

int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=m;j++)
		{
			scanf("%d",&a[i][j]);
			gs++;b[gs]=a[i][j];x[gs]=i;y[gs]=j;
		}
	}
	qsort(1,gs);
	int l=0;
	for (int i=1;i<=gs;i++)
	{
		if (b[i]!=b[i-1]) l++;
		c[x[i]][y[i]]=l;
	}
	up=l;
	zh=0;
	sum=0;ans=0;
	for (register int j=1;j<=k;j++)
	{
		int ppp=0;
		for (register int l=1;l<=k;l++)
		{
			p=a[j][l];
			modify(c[j][l],p,1);
			sum+=p;zh++;
			
			jl=0;gg=0;
			p=a[j][l];
			getans(c[j][l]);
			ans=(ans+gg*p%mo-jl+mo)%mo;
			ans=(ans+(sum-jl+mo)%mo-(zh-gg)*p%mo+mo)%mo;
		}
	}
	answer=(answer+ans*2%mo+mo)%mo;
	for (register int i=1;i<=(n-k+1);i++)
	{
//		printf("%lld\n",answer);
		if (i%2==1)
		{
			for (register int j=2;j<=m-k+1;j++)
			{
				for (register int l=i;l<=k+i-1;l++)
				{
					jl=0;gg=0;
					p=a[l][j-1];
					getans(c[l][j-1]);
					ans=(ans-gg*p%mo+jl+mo)%mo;
					ans=(ans-(sum-jl+mo)%mo+(zh-gg)*p%mo+mo)%mo;
					
					p=a[l][j-1];
					modify(c[l][j-1],p,-1);
					sum=(sum-p+mo)%mo;zh--;
				}
				for (register int l=i;l<=k+i-1;l++)
				{
					p=a[l][j+k-1];
					modify(c[l][j+k-1],p,1);
					sum=(sum+p+mo)%mo;zh++;
					
					jl=0;gg=0;
					p=a[l][j+k-1];
					getans(c[l][j+k-1]);
					ans=(ans+gg*p%mo-jl+mo)%mo;
					ans=(ans+(sum-jl+mo)%mo-(zh-gg)*p%mo+mo)%mo;
				}
				answer=(answer+2*ans+mo)%mo;
			}
			if (i<n-k+1)
			{
				for (register int l=m-k+1;l<=m;l++)
				{
					jl=0;gg=0;
					p=a[i][l];
					getans(c[i][l]);
					ans=(ans-gg*p%mo+jl+mo)%mo;
					ans=(ans-(sum-jl+mo)%mo+(zh-gg)*p%mo+mo)%mo;
					
					p=a[i][l];
					modify(c[i][l],p,-1);
					sum=(sum-p+mo)%mo;zh--;
				}
				for (register int l=m-k+1;l<=m;l++)
				{
					p=a[i+k][l];
					modify(c[i+k][l],p,1);
					sum=(sum+p+mo)%mo;zh++;
					
					jl=0;gg=0;
					p=a[i+k][l];
					getans(c[i+k][l]);
					ans=(ans+gg*p%mo-jl+mo)%mo;
					ans=(ans+(sum-jl+mo)%mo-(zh-gg)*p%mo+mo)%mo;
				}
				answer=(answer+2*ans+mo)%mo;
			}
		}
		else
		{
			for (register int j=m-k;j>=1;j--)
			{
				for (register int l=i;l<=k+i-1;l++)
				{
					jl=0;gg=0;
					p=a[l][j+k];
					getans(c[l][j+k]);
					ans=(ans-gg*p%mo+jl+mo)%mo;
					ans=(ans-(sum-jl+mo)%mo+(zh-gg)*p%mo+mo)%mo;
					
					p=a[l][j+k];
					modify(c[l][j+k],p,-1);
					sum=(sum-p+mo)%mo;zh--;
				}
				for (register int l=i;l<=k+i-1;l++)
				{
					p=a[l][j];
					modify(c[l][j],p,1);
					sum=(sum+p+mo)%mo;zh++;
					
					jl=0;gg=0;
					p=a[l][j];
					getans(c[l][j]);
					ans=(ans+gg*p%mo-jl+mo)%mo;
					ans=(ans+(sum-jl+mo)%mo-(zh-gg)*p%mo+mo)%mo;
				}
				answer=(answer+2*ans+mo)%mo;
			}
			if (i<n-k+1)
			{
				for (register int l=1;l<=k;l++)
				{
					jl=0;gg=0;
					p=a[i][l];
					getans(c[i][l]);
					ans=(ans-gg*p%mo+jl+mo)%mo;
					ans=(ans-(sum-jl+mo)%mo+(zh-gg)*p%mo+mo)%mo;
					
					p=a[i][l];
					modify(c[i][l],p,-1);
					sum=(sum-p+mo)%mo;zh--;
				}
				for (register int l=1;l<=k;l++)
				{
					p=a[i+k][l];
					modify(c[i+k][l],p,1);
					sum=(sum+p+mo)%mo;zh++;
					
					jl=0;gg=0;
					p=a[i+k][l];
					getans(c[i+k][l]);
					ans=(ans+gg*p%mo-jl+mo)%mo;
					ans=(ans+(sum-jl+mo)%mo-(zh-gg)*p%mo+mo)%mo;
				}
				answer=(answer+2*ans+mo)%mo;
			}
		}
	}
	printf("%lld\n",answer);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值