GalaxyOJ-725 (二分+容斥)

题目

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 3

Sample 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值