第二章 前缀和、差分与离散化 例题

第二章 前缀和、差分与离散化 例题

*T1 P1719 最大加权矩形

题目描述:

给定一个n*n的矩阵,求出其中元素和最大的一块子矩阵的元素和。

\(n\le120\)

思路1: 矩阵前缀和 \(O(n^4)\)

先执行一次矩阵前缀和。

然后依次枚举每对点,\(O(1)*n^4\)遍历每一个子矩阵。

*思路二: \(O(n^3)\)

枚举起始行(行a),然后从这行Qwq开始往后枚举终止行(行b)。

对于每个行a的每一纵列执行前缀和,然后在求和到某一行b时,从左到右dp寻找恰好包含a~b所有行的矩阵中的最大子矩阵,再在其中寻找最大值即可。

Code:

#include<bits/stdc++.h>
#define ll long long
#define fr(i,r) for(int i=1;i<=r;++i)
#define For(i,l,r) for(int i=l;i<=r;++i)
#define Rof(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N=130,Inf=0x7fffffff;
char cch;
int res,zf;
inline int rd()
{
	while((cch=getchar())<45);
	if(cch^45)res=cch^48,zf=1;
	else res=0,zf=-1;
	while((cch=getchar())>=48)res=(res*10)+(cch^48);
	return res*zf;
}

int n=rd(),mat[N][N];
int sum[N];
int dplst,dppos,mx=-Inf;

int main()
{
	fr(i,n)fr(j,n)mat[i][j]=rd();
	fr(i,n)//起始行
	{
		memset(sum,0,sizeof(sum));
		For(j,i,n)//终止行
		{
			dplst=0;
			fr(k,n)
			{
				sum[k]+=mat[j][k];
				dppos=max(dplst+sum[k],sum[k]);
				mx=max(mx,dppos);
				dplst=dppos;
			}
		}
	}
	cout<<mx;
	return 0;
}

T2 P

题目描述:

思路1:

思路2:

Code:


T3 P2367 语文成绩

题目描述:

给定一个n项数列,p次修改,每次将一个给定区段都加上一个数值,求最后数列中的最小值。

\(p\le n\le 5*10^6\)

思路:

差分裸题。

Code:

#include<bits/stdc++.h>
#define ll long long
#define fr(i,r) for(int i=1;i<=r;++i)
#define For(i,l,r) for(int i=l;i<=r;++i)
#define Rof(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N=5e6+10,Inf=0x7fffffff;
char cch;
int res,zf;
inline int rd()
{
	while((cch=getchar())<45);
	if(cch^45)res=cch^48,zf=1;
	else res=0,zf=-1;
	while((cch=getchar())>=48)res=(res*10)+(cch^48);
	return res;
}

int n=rd(),p=rd(),a[N],c[N];
int x,y,z;
int mi=Inf;

int main()
{
	fr(i,n) a[i]=rd();
	while(p--)
	{
		x=rd(),y=rd(),z=rd();
		c[x]+=z,c[y+1]-=z;
	}
	fr(i,n)
	{
		c[i]+=c[i-1];
		mi=min(mi,c[i]+a[i]);
	}
	cout<<mi;
	return 0;
}

T4 P3397 地毯

题目描述:

一个n*n的矩阵,初始为0。m次修改,每次将一个给定范围内的所有数+1,求最后的矩阵。

\(n,m\le1000\)

思路:

逐行差分。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,a[1002][1002],xs,ys,xe,ye,sum;
int main()
{
	scanf("%d%d",&n,&m);
	for(i=0;i<m;i++)
	{
		scanf("%d%d%d%d",&xs,&ys,&xe,&ye);
		for(j=xs;j<=xe;j++)
		{
			a[j][ys]++;
			a[j][ye+1]--;
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			a[i][j]+=a[i][j-1];
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
}

*T5 P1496 火烧赤壁

题目描述:

给定n个区间a~b,求这些区间覆盖的总长度。

\(1≤n≤2×10^4\ ,\ -2^{31} \le a \le b \lt 2^{31}\)

思路1:

image

如上图所示,上方的情况可以转换成下方的情况。

所以起点终点的顺序对答案不产生影响。

于是对起点和终点分别进行排序,累加每一条线段的长度。若与前一条线段有重复区域则减去重复部分。

思路2: 离散化

</>

Code: 思路一

#include<bits/stdc++.h>
#define ll long long
#define fr(i,r) for(int i=1;i<=r;++i)
#define For(i,l,r) for(int i=l;i<=r;++i)
#define Rof(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N=2e4+10,Inf=0x7fffffff;
char cch;
int res,zf;
inline int rd()
{
	while((cch=getchar())<45);
	if(cch^45)res=cch^48,zf=1;
	else res=0,zf=-1;
	while((cch=getchar())>=48)res=(res*10)+(cch^48);
	return res*zf;
}

int n=rd();
ll ans;
int a[N],b[N];

int main()
{
	fr(i,n)a[i]=rd(),b[i]=rd();
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	
	b[0]=-Inf;
	fr(i,n)
	{
		ans+=(b[i]-a[i]);
		if(b[i-1]>a[i])ans-=(b[i-1]-a[i]);
	}
	cout<<ans;
	return 0;
}

!<离散化> T6 P1955 [NOI2015] 程序自动分析

题目描述:

给定n组形如 \(a_i=a_j\) , \(a_1\not=a_j\) 的约束条件,判断所有约束条件间是否存在矛盾。

\(共\ \ T\le10\ \ 组数据,n\le10^6,i,j\le10^9\)

思路:

离散化+并查集。

先将所有相等的数相联通,对于不等于的约束直接判断父亲是否相同即可。

Code:

#include<bits/stdc++.h>
#define ll long long
#define fr(i,r) for(int i=1;i<=r;++i)
#define For(i,l,r) for(int i=l;i<=r;++i)
#define Rof(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N=1e6+10,Inf=0x7fffffff;
char cch;
int res,zf;
inline int rd()
{
	while((cch=getchar())<45);
	if(cch^45)res=cch^48,zf=1;
	else res=0,zf=-1;
	while((cch=getchar())>=48)res=(res*10)+(cch^48);
	return res*zf;
}

int T=rd();
int n;

struct ques
{
	int x,y,opt;
}a[N];


int fa[N<<1];//初始化到2n!最多会出现2n个不同数
inline int getf(int v)
{
	if(v==fa[v])return v;
	return fa[v]=getf(fa[v]);
}
inline void combine(int x,int y)
{
	x=getf(x),y=getf(y);
	if(x^y)fa[x]=y;
}

int ls[N<<1],p;
inline void lsh()
{
	sort(ls+1,ls+n*2+1);//离散化需要先进行排序
	p=unique(ls+1,ls+n*2+1)-ls;//p记录为不重项末项的下一位下标
	fr(i,n)
	{
		a[i].x=lower_bound(ls+1,ls+p,a[i].x)-ls;//在去重后的有序数列中,第一个大于或等于一个数的位置即为它的相对大小
		a[i].y=lower_bound(ls+1,ls+p,a[i].y)-ls;
	}
}

inline bool cmp(ques xx,ques yy)
{
	return xx.opt>yy.opt;
}
int main()
{
	while(T--)
	{
		memset(a,0,sizeof(a));
		n=rd();
		fr(i,n)
		{
			a[i].x=rd(),a[i].y=rd();
			a[i].opt=rd();
			ls[(i<<1)-1]=a[i].x;//将数据载入离散化数组
			ls[i<<1]=a[i].y;
		}
		
		lsh();
		
		sort(a+1,a+n+1,cmp);//把'='操作放到前面
		fr(i,p)fa[i]=i;//并查集重置
		
		int i=1;
		while(a[i].opt) combine(a[i].x,a[i].y),++i;
		
		bool flag=1;
		while(i<=n)
		{
			if(getf(a[i].x)==getf(a[i].y))
			{
				flag=0;
				cout<<"NO\n";
				break;
			}
			++i;
		}
		if(flag)cout<<"YES\n";
	}
	return 0;
}

! 离散化:

将所有出现的数存入一个单独的离散化数组,先进行排序,利用unique函数去除重复出现的数。再进行排序,排序后每个数值所对应的相对大小即对应着其在新数组中的下标。

T7 P

题目描述:

思路:

Code:


posted @ 2022-04-30 08:45  penggeng  阅读(56)  评论(0编辑  收藏  举报