第二章 前缀和、差分与离散化 例题
第二章 前缀和、差分与离散化 例题
*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:
如上图所示,上方的情况可以转换成下方的情况。
所以起点终点的顺序对答案不产生影响。
于是对起点和终点分别进行排序,累加每一条线段的长度。若与前一条线段有重复区域则减去重复部分。
思路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: