题目
Problem Description
wzj 同学在和同学们玩他新创作的游戏。游戏规则是这样的: 游戏依靠一个 n 行 m 列的矩阵进行,矩阵中的每一个元素都是非负 整数,wzj 是“管理员”,而其他同学是“玩家”。游戏开始时,管 理员给出两个整数 P 和 S,每一个玩家选择一个矩形区域,然后把矩 形区域内所有≥P 的元素个数与他们的和相乘,这就是这个玩家的 得分。如果所有玩家的得分之和与 S 之差的绝对值小于某一个值 D,管理员就会胜利,反之则玩家胜利。wzj 当然想赢,因此他通过 某种方式(不要管他怎么知道的)得知了所有玩家将要选择的矩形 区域,他想知道,在 S 和 D 一定的情况下,他能否胜利,以及所有 玩家的得分之和与 S 之差的绝对值最小是多少。
Input
输入有多行。第一行有 4 个整数 n,m,S,D,含义如题目描述中 所示。
接下来 n 行,每行 m 个正整数,表示矩阵中的元素。 接下来一行,有一个整数 q,为玩家的个数。
接下来 q 行,每行四个整数 x1,y1,x2,y2,为玩家选择的矩形区域的左上角坐标和右下角坐标。1≤n,m≤1000,1≤q≤200000,1≤x1,x2 ≤n,1≤y1,y2≤m,0≤矩阵中的元素≤1000,0≤S,D≤10^13
Output
第一行,如果 wzj 能胜利,输出“Yes”,否则输出“No”(引号 不用输出)。
第二行,输出一个整数,为所有玩家的得分之和与 S 之差的绝 对值的最小值。
Sample Input
3 3 50 20
1 2 3
2 3 4
3 4 5
2
1 1 2 3
2 2 3 3Sample Output
Yes
7样例说明
P≤1 时,得分之和=(1+2+3+2+3+4)*6+(3+4+4+5)*4=154,差值 为 104。
P=2 时,得分之和=(2+3+2+3+4)*5+(3+4+4+5)*4=134,差值为 84。
P=3 时,得分之和=(3+3+4)*3+(3+4+4+5)*4=94,差值为 44。 P=4 时,得分之和=4*1+(4+4+5)*3=43,差值为 7。
P=5 时,得分之和=0*0+5*1=5,差值为 45。
P>5 时,得分之和=0*0+0*0=0,差值为 50。因此,当 P 取 4 时差值最小,为 7,且 7<20,所以 wzj 能胜 利。
分析
- 将 P 二分,则明显 P 越大,每个人得分和-S 的值越小。
- 利用容斥预处理当前 P 下子矩阵中大于等于 p 的元素个数以及它们的和,之后就求出每个人在当前 P 下得分的和与 S 的差。(之所以不直接暴力,是因为数据范围不允许)
程序
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Mid ((l+r)>>1)
long long n,m,l,r,S,D,P,q,x1[200005],y1[200005],x2[200005],y2[200005];
long long a[1005][1005],b[1005][1005],c[1005][1005]; //原数,个数,和
long long k,ans;
long long ok(int p){ //此 p 的分数和与 S 之差
long long sum=0;
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++){
c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1];
b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
if (a[i][j]>=p){
b[i][j]++;
c[i][j]+=a[i][j];
}
}
for (int i=1; i<=q; i++)
sum+=(c[x2[i]][y2[i]]-c[x1[i]-1][y2[i]]-c[x2[i]][y1[i]-1]+c[x1[i]-1][y1[i]-1])*(b[x2[i]][y2[i]]-b[x1[i]-1][y2[i]]-b[x2[i]][y1[i]-1]+b[x1[i]-1][y1[i]-1]);
return sum-S;
}
int main(){
freopen("1.txt","r",stdin);
scanf("%lld%lld%lld%lld",&n,&m,&S,&D);
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
scanf("%lld",&a[i][j]);
scanf("%lld",&q);
for (int i=1; i<=q; i++)
scanf("%lld%lld%lld%lld",&x1[i],&y1[i],&x2[i],&y2[i]);
l=0,r=1000;
while (l<=r){
if ((k=ok(Mid))>0){
ans=k;
P=Mid;
l=Mid+1;
}
else r=Mid-1;
}
k=-ok(P+1);
ans=min(ans,k);
if (ans>=D) puts("No"); else puts("Yes");
printf("%lld",ans);
}