HOLLOW - Editorial

See, most of the binary search problems that i do need to find the leftmost element which is \geq something, or rightmost element \leq something.
Take this problem for example, we need to find if a square of size k is possible, and need the maximum value of k.
I do this-

int l=1,r=n,mid,ans=0;    //you can take ans to be the default if answer isn't possible for any value(depends on problem)
while(l<=r)
{
    mid=(l+r)>>1;
    if(possible(mid))
    {
        ans=mid;
        l=mid+1;
    }
    else r=mid-1;
}
System.out.print(ans);

If you do this, you will need an extra ans variable, but the problem you are facing shouldn’t exist.

Check line 36 of my code for this problem.

4 Likes
CODE
// #include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <string>
#include <cstring>
#include <random>
#include <bitset>
using namespace std;
#define vt vector 
#define ar array 
#define sz(a) (int)a.size() 
#define ll long long 
// debugger credits: https://codeforces.com/blog/entry/68809 
//#pragma GCC optimize "trapv"
#define F_OR(i, a, b, s) for (int i = (a); ((s) > 0 ? i < (b) : i > (b)); i += (s))
#define F_OR1(e) F_OR(i, 0, e, 1)
#define F_OR2(i, e) F_OR(i, 0, e, 1)
#define F_OR3(i, b, e) F_OR(i, b, e, 1)
#define F_OR4(i, b, e, s) F_OR(i, b, e, s)
#define GET5(a, b, c, d, e, ...) e
#define F_ORC(...) GET5(__VA_ARGS__, F_OR4, F_OR3, F_OR2, F_OR1)
#define FOR(...) F_ORC(__VA_ARGS__)(__VA_ARGS__)
#define EACH(x, a) for (auto& x: a)
template<class T> bool umin(T& a, const T& b) {
	return b<a?a=b, 1:0;
}
template<class T> bool umax(T& a, const T& b) { 
	return a<b?a=b, 1:0;
}
template<class A> void read(vt<A>& v);
template<class A, size_t S> void read(ar<A, S>& a);
template<class T> void read(T& x) {
	cin >> x;
}
void read(double& d) {
	string t;
	read(t);
	d=stod(t);
}
void read(long double& d) {
	string t;
	read(t);
	d=stold(t);
}
template<class H, class... T> void read(H& h, T&... t) {
	read(h);
	read(t...);
}
template<class A> void read(vt<A>& x) {
	EACH(a, x)
		read(a);
}
template<class A, size_t S> void read(array<A, S>& x) {
	EACH(a, x)
		read(a);
}
const int mxN=1e5,di[4]={1,0,-1,0},dj[4]={0,-1,0,1};
void solve(){		
	int n,m,k ;
	read(n,m,k) ;
	vt<string>a(n) ;read(a) ;
	vt<vt<int>>dp(n+2,vt<int>(m+2)),dp2(n+2,vt<int>(m+2));
	int c=0 ;
	FOR(i,1,n+1)
		FOR(j,1,m+1){
			c+=a[i-1][j-1]=='0' ;
			dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+(a[i-1][j-1]=='1') ;
			dp2[i][j]=dp2[i-1][j]+dp2[i][j-1]-dp2[i-1][j-1]+(a[i-1][j-1]=='0') ;
		}
	int ans = -1 ;
	FOR(i,1,n+1)
		FOR(j,1,m+1){
			int lb=1,rb=min(n-i+1,m-j+1) ;
			while(lb<=rb){
				int mb=(lb+rb)/2 ;
				int lx=i,ly=j ;
				int rx=min(i+mb-1,n),ry=min(j+mb-1,m) ;
				int A=dp[rx][ry]-dp[rx][ly-1]-dp[lx-1][ry]+dp[lx-1][ly-1] ;
				int Z=dp2[rx][ry]-dp2[rx][ly-1]-dp2[lx-1][ry]+dp2[lx-1][ly-1] ;
				Z=c-Z ;
				bool ok=1 ;
				if(A>Z)
					ok=0 ;
				if(A>k)
					ok=0 ;
				if(ok)
					lb=mb+1 ;
				else 
					rb=mb-1 ;
			}
			umax(ans,lb-1) ;
		}
	cout<<ans<<'\n';

}
signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  //cout << setprecision(20) << fixed ;
  int T=1;
	read(T);
	FOR(_,T){
		// pff("Case #", _+1, ": ");
		solve();
	}
	return 0;
}


:man_facepalming: Now this one got accepted, I only changed the < to <= and rb=mb-1. I wish someone would’ve told this to me earlier. Thanks a ton!!

3 Likes

such things happen pal, I remember running a binary search in the opposite direction(reversing conditions of l=mid+1 and r=mid-1) on multiple occasions

2 Likes

So is this the generic binary search template you use for all binary search problems or you manipulate the <= or < ,r=m-1 or r=m etc… according to the problem ?

1 Like

i don’t use any templates, i write one according to the problem

2 Likes

Can someone explain the last problem solution. I found no editorial for it.

  1. take all points from all possible line segments of A and B
  2. sort all of them
  3. select the unique points
  4. assign them appropriate values fr eg set of pts=[1,6,8,10], corresp values=[1,2,3,4]
  5. do the same thing u do when u have to add +1 to a segment, ie mark starting indices as +1, ending as -1, and perform cumulative sum
  6. any point on this newly formed array now indicates number of lines moving from pt(i) to pt(i+1), so the value it can contribute will be [pt(i+1)-pt(i)]*(number of lines)
  7. perform prefix sum
  8. find value for all ranges of B in O(1) using the prefix sum
    The total sum is your answer. Time complexity is NlogN.

The code.

1 Like
1 Like

Oh, so sorry that I was not able to find the editorial.

// I AM TRYING TO DEBUGGING MY CODE FROM LAST 2 HRS BUT NOT ABLE THAT WHERE I AM WRONG .PLEASE SOMEONE HELP ME! THNX


import java.util.*;
import java.lang.*;


 public class Main
{
static int[][] arr ;
static int[][] dp ;
static int n,m,k,c0,c1 ;
 

public static boolean check(int a)
{
    
    if(a*a > c0)return false  ;
    
    
     for(int i = 1 ; i <= n-a+1 ;i++)
     {
         for(int j = 1 ; j <= m-a+1 ;j++)
         {
           int x = i+a-1 ;int y = j+a-1 ;  
           
             int val1 = dp[x][y] ;
             int val2  = dp[i-1][j-1] ;
             int val3  = dp[i-1][y] ;
             int val4  = dp[x][j-1] ;
             
             
             int val = val1 -val3-val4+val2  ;
             
             val =  (a*a) -val ;
             
             if(val <= k)return true  ;
             
         }
}
    

return false  ;
    
}


public static void solve()
{

Scanner scn = new Scanner(System.in) ; 

int testcase = 1;
 testcase = scn.nextInt() ;
for(int testcases =1  ; testcases <= testcase ;testcases++)
{
   

n= scn.nextInt() ; m= scn.nextInt() ; k= scn.nextInt() ;

arr = new int[n+10][m+10] ;dp = new int[n+10][m+10] ;


for(int i = 1 ; i <= n ;i++)
{
    String s = scn.next() ;
    s= "%" + s ;
    
    for(int j = 1 ; j <= m ;j++)
    {
        if(s.charAt(j) == '1')arr[i][j] = 1;
        else arr[i][j] = 0 ;
        
        if(arr[i][j] == 0)c0++ ;
        else c1++ ;
        
    }
    
    
    
}


if(arr[1][1] == 0)dp[1][1] =1 ;
else dp[1][1] =0;


for(int j = 2 ; j <= m;j++)
{
    dp[1][j] = dp[1][j-1] ;
    
    if(arr[1][j] == 0)dp[1][j]++ ;
}



for(int i = 2 ; i <= n;i++)
{
    dp[i][1] = dp[i-1][1] ;
    
    if(arr[i][1] == 0)dp[i][1]++ ;
}





for(int i = 2 ; i <= n ; i++)
{
    for(int j = 2; j <= m ;j++)
    {
        
        dp[i][j] = dp[i-1][j] + dp[i][j-1] -dp[i-1][j-1] ;
        
         if(arr[i][j] == 0)dp[i][j]++ ;
        
    }
    
}



int s = 0 ;int e  = Math.min(n,m) ;

int ans = 0 ;

while(s <= e)
{
    int m = (s+e)/2 ;
    
    
    if(check(m))
    {
        ans = m ;
        s=m+1 ;
    }
    
    else{
        
        e=m-1  ;
    }
    
    
    
    
}


System.out.println(ans) ;

} 

    
} 


public static void main (String[] args) throws java.lang.Exception
{
  

solve() ;
      
}


}

IT CAN LOOK A BIT LARGE AT FIRST GLANCE BUT IT IS SAME ,CHECK Fn CHECKING THE CONDITION AND REST JUST PREFIX CALCULATION .

For calculating mid. The best practice is to use mid = r-l / 2 + l
This will prevent overflow in case of l+r

2 Likes

Read it you will never screw again.

2 Likes

Thanks a lot for sharing this!!

Oh I see you have gained a star, congrats on being 5 star cube. :slight_smile:

1 Like

Thank you taran for also providing links to get more information about the 2-D prefix sum.

Help anyone? My code

What about this test case-
1 1 1

2 Likes

Cheers

1 Like

Hey there, I think we can also reduce the factor of log2() from time complexity.
Please correct me if I am wrong.

Idea

we can search for a square from a given vertex in increasing order.
i.e. if MAX sq. size of p is founded till now, then it needs not search for a size smaller than p.
I guess it’s time complexity will be o(n*m+min(n,m));

My code
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (ll i = (a); i <= (b); i++)
#define fast_io ios_base::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
#define read(a) ll a; cin >> a;
#define ll long long int
#define endl '\n';

ll aux[1005][1005];
ll mat[1005][1005];
ll sumQuery(int tli, int tlj, int rbi,int rbj) 
{ 
  int res = aux[rbi][rbj]; 
  if (tli > 0) res = res - aux[tli-1][rbj]; 
  if (tlj > 0) res = res - aux[rbi][tlj-1]; 
  if (tli > 0 && tlj > 0)  res = res + aux[tli-1][tlj-1]; 
  return res; 
} 

int main()
{
  fast_io;
  //“Don’t wish it were easier. Wish you were better.” – Jim Rohn;
  read(t);
  while(t--)
  {
    ll cnt=0;
    ll m,n,k; cin>>m>>n>>k;
    char c;
    FOR(i,0,m-1) FOR(j,0,n-1)
    {
      cin>>c;
      if(c=='1') mat[i][j]=1;
      else cnt++,mat[i][j]=0;
      aux[i][j]=0;
    }
    FOR(i,0,n-1)  aux[0][i] = mat[0][i]; 
    FOR(i,1,m-1) FOR(j,0,n-1) aux[i][j] = mat[i][j] + aux[i-1][j]; 
    FOR(i,0,m-1) FOR(j,1,n-1)aux[i][j] += aux[i][j-1]; 

    ll sq_size=0;
    FOR(i,0,m-1) FOR(j,0,n-1) FOR(p,sq_size+1,min(n,m))
    {
     if(i+p-1>m-1 or j+p-1>n-1) break;
     ll sum=sumQuery(i,j,i+p-1,j+p-1);
     if(sum<=k and p*p<=cnt) sq_size++;
     else break;
    }
    cout<<sq_size<<endl;
 }

}