2020.01.29日常总结兼二维前缀和、二阶差分略讲

\color{green}{二维前缀和}

二维前缀和,就是前缀和算法的二维形式,其基本思想与前缀和算法类似。

我们记 s u m i , j sum_{i,j} 表示二维数组 a i , j a_{i,j} 的二维前缀和,则 s u m i , j = sum_{i,j}=

i = 1 N j = 1 M a i , j \sum\limits_{i=1}^{N} \sum\limits_{j=1}^{M}a_{i,j}

让我们考虑如何在 O ( N × M ) O(N \times M) 的时间复杂度内求出 s u m sum 数组。
在这里插入图片描述
如图, s u m i , j = a i , j + s u m i , j 1 + s u m i 1 , j s u m i 1 , j 1 sum_{i,j}=a_{i,j}+sum_{i,j-1}+sum_{i-1,j}-sum_{i-1,j-1} (自行配图理解)

至于,如何求 i = L 1 L 2 j = R 1 R 2 a i , j \sum\limits_{i=L_1}^{L_2} \sum\limits_{j=R_1}^{R_2} a_{i,j} 的和,大家自行画图想想。


\color{green}{二阶差分}

所谓的二阶差分,就是差分后再差分的意思。它的计算很简单,就是先差分一次,在对差分数组再差分一次。

重点在于如何利用二阶差分数组还原原数组。其实也很简单,就是两次前缀和即可。第一次前缀和求出差分数组,再一次前缀和就求出了原数组。


\color{green}{例题}

P 3138    [ U S A C O 16 F E B ] L o a d   B a l a n c i n g   S i l v e r \color{blue}{洛谷P3138\ \ [USACO16FEB]负载平衡Load\ Balancing\ Silver}

\color{orange}{【题意】:} 给你一个矩阵,里面有些点,让你横向切一刀,纵向切一刀,使得得到的四个区域内的最大的点数最少。

\color{orange}{【思路】:} 先对点的坐标离散化,然后直接用二维前缀和即可。

\color{orange}{【代码】:}

struct node{
	int x,y,nx,ny,sub;
//	x,y:输入时的坐标
//	nx,ny:离散化后的坐标 
	void read_itself(int i){
		scanf("%d%d",&x,&y);
		sub=i;nx=ny=0;
	}
}a[1100];int n,ans;
int sum[1100][1100];
inline bool cmp1(node a,node b){
	return a.x<b.x;
}
inline bool cmp2(node a,node b){
	return a.y<b.y;
}
inline bool cmp3(node a,node b){
	return a.sub<b.sub;
}//按不同的关键字对a数组排序 
int main(){
	freopen("t1.in","r",stdin);
	scanf("%d",&n);ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
		a[i].read_itself(i);
	sort(a+1,a+n+1,cmp1);
	a[1].nx=1;//注意离散化的初始化
	for(int i=2;i<=n;i++)
		if (a[i].x==a[i-1].x)
			a[i].nx=a[i-1].nx;
		else a[i].nx=a[i-1].nx+1;
	sort(a+1,a+n+1,cmp2);
	a[1].ny=1;//注意离散化的初始化
	for(int i=2;i<=n;i++)
		if (a[i].y==a[i-1].y)
			a[i].ny=a[i-1].ny;
		else a[i].ny=a[i-1].ny+1;
	sort(a+1,a+n+1,cmp3);
	for(int i=1;i<=n;i++)
		sum[a[i].nx][a[i].ny]++;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			ans=min(ans,max(max(sum[i][j],sum[i][n]-sum[i][j]),max(sum[n][j]-sum[i][j],sum[n][n]-sum[i][n]-sum[n][j]+sum[i][j])));
	printf("%d",ans);
	return 0;
}

P 5026    L y c a n t h r o p y \color{blue}{洛谷P5026\ \ Lycanthropy}

\color{orange}{【题意】:}
在这里插入图片描述
\color{orange}{【思路】:} 二阶差分的模板题。

做二阶差分的题,只需把表格列出来,一个一个分析即可。这里为了节约篇幅,不给出表格。

\color{orange}{【代码】:}

const int N=3e6+100;
#define ll long long
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
ll d[N],p[N],t[N],n,m;
void write(ll a,bool b){
	if (a<0){
		putchar('-');
		write(-a,false);
	}
	else if (a==0){
		if (b) putchar('0');
	}
	else{
		write(a/10,false);
		putchar(a%10+'0');
	}
}
void print(ll a){
	write(a,true);
	putchar(' ');
}
const int P=30000;
int main(){
	m=read();n=read();
	for(int i=1;i<=m;i++){
		register int a,b;
		b=read();a=read();
		d[a-3*b+1+P]++;
		d[a-2*b+1+P]-=2;
		d[a+1+P]+=2;
		d[a+2*b+1+P]-=2;
		d[a+3*b+1+P]++;
	}
	for(int i=1;i<=n+P;i++)
		p[i]=p[i-1]+d[i];
	for(int i=1;i<=n+P;i++)
		t[i]=t[i-1]+p[i];
	for(int i=P+1;i<=n+P;i++)
		print(t[i]);
	return 0;
}
发布了103 篇原创文章 · 获赞 4 · 访问量 6741

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/104106304