bzoj 1127 贪心+悬线法

25 篇文章 0 订阅
3 篇文章 0 订阅

题意:给定一个n*n的矩阵,求一个子矩阵满足权值和属于[k,2*k] (special judge )

思路棒极了(┙>∧<)┙へ┻┻

对一个一维的[l,r],如果这个区间满足 任取x, x<k 且 区间和>=k ,那么答案肯定存在在这个区间中

证明:

因为这个区间里,任意x均满足x<k,所以每加上一个数,区间和的变化量属于[0.k)

所以不会存在一个数,使得一个连续的子区间的区间和加上它以后直接从小于k越过[k,2*k]的区间,瞬间达到大于k

所以只要找到这个[l,r],并从这个区间的左端或右端开始一一删除判断,就可以得到答案的区间

虽然这是一维下的结论,但是这个结论是可以拓展的ヽ(•̀ω•́ )ゝ

即:一个子矩阵满足 任取x,x<k 且 区间和>=k,那么答案肯定存在在这个子矩阵里

所以只要找到这个子矩阵,就可以类比上面的方法删一删就能得到答案的区间,具体:

(1)如果是一行的话,退化成了一维问题,只要从左端或右端开始一一删除判断即可

(2)如果是个子矩阵的话,从最上一行或最下一行一行一行的删除,直到删得还剩一行,按上面的继续处理

然后就是要O(n^2)的找到子矩阵,把大于2*k的点看做障碍点,用悬线法,得到最大子矩阵,一一判断即可

当然,如果一开始读入的时候,如果一个1*1的位置就已经满足[k,2*k],直接输出即可~(~o ̄▽ ̄)~o 。。。

var
        n,m             :longint;
        flag            :boolean;
        sum,map         :array[0..2010,0..2010] of int64;
        l,r,h           :array[0..2010,0..2010] of longint;
        i,j             :longint;
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

function get_sum(x1,y1,x2,y2:longint):int64;
begin
   exit(sum[x2,y2]-sum[x1-1,y2]-sum[x2,y1-1]+sum[x1-1,y1-1]);
end;

function check:boolean;
var
        i,j:longint;
begin
   for i:=1 to n do
     for j:=1 to n do
       if (map[i,j]>=m) and (map[i,j]<=2*m) then
       begin
          writeln(j,' ',i,' ',j,' ',i);
          exit(true);
       end;
   exit(False);
end;

procedure cut(x1,y1,x2,y2:longint);
var
        i,j:longint;
begin
   while get_sum(x1,y1,x2,y2)>2*m do
   begin
      if x1=x2 then dec(y2) else
        if get_sum(x1+1,y1,x2,y2)>=m then inc(x1) else dec(x2);
   end;
   flag:=true;
   writeln(y1,' ',x1,' ',y2,' ',x2);
end;

begin
      read(m,n);
      for i:=1 to n do
        for j:=1 to n do read(map[i,j]);
      if not check then
      begin
         for i:=1 to n do
           for j:=1 to n do sum[i,j]:=map[i,j]+sum[i,j-1]+sum[i-1,j]-sum[i-1,j-1];
         for i:=1 to n do
         begin
            for j:=1 to n do
             if (map[i,j]>2*m) then l[i,j]:=0 else l[i,j]:=l[i,j-1]+1;
            for j:=n downto 1 do
              if (map[i,j]>2*m) then r[i,j]:=0 else r[i,j]:=r[i,j+1]+1;
         end;
         //
         for i:=2 to n do
           for j:=1 to n do
             if (map[i,j]<=2*m) and (map[i-1,j]<=2*m) then
             begin
                h[i,j]:=h[i-1,j]+1;
                l[i,j]:=min(l[i,j],l[i-1,j]);
                r[i,j]:=min(r[i,j],r[i-1,j]);
             end;
         //
         for i:=1 to n do
           for j:=1 to n do
             if (map[i,j]<=2*m) then
               if (get_sum(i-h[i,j],j-l[i,j]+1,i,j+r[i,j]-1)>=m) then
               begin
                  cut(i-h[i,j],j-l[i,j]+1,i,j+r[i,j]-1);break;
               end;
         if not flag then writeln('NIE');
      end;
end.
——by Eirlys



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值