Автор проблеми: | De3b0o |
Підготовлено: | De3b0o |
Редаговано: | Hasan Saleh (MathModel) |
Для кожної рівної клітинки ми попередньо обчислюємо горизонтальний інтервал у її рядку, який можна досягти без перетворення пагорба. Нехай l[i][j] буде найлівішою колонкою, доступною з у рядку , а r[i][j] буде найправішою. Ці значення обчислюються шляхом сканування кожного рядка зліва направо та справа наліво. Таким чином, залишаючись у тому ж рядку, клітинка може переміститися в будь-яку колонку в
Оскільки граф переміщення є ненаправленим (якщо ми можемо переміститися з до , то ми також можемо переміститися з до ), ми запускаємо BFS, починаючи з клітинки концерту (n_1,m_1). Нехай \text{ans}[i][j] позначає мінімальну кількість переміщень, необхідних для досягнення концерту з . Для кожного рядка ми підтримуємо множину, що містить усі неперевірені колонки в цьому рядку.
Обробляючи стан BFS , спочатку обробляємо рядок сам по собі. Кожна неперевірена клітинка в колонках [l[x][y], r[x][y]] може бути досягнута за один хід, тому ми присвоюємо їм відстань \text{ans}[x][y]+1 і додаємо їх у чергу BFS.
Потім ми розширюємо вгору. Для рядків i = x+1, x+2, \dots, поки клітинка є рівною і або неперевіреною, або має строго більшу відстань, ніж \text{ans}[x][y], ми підтримуємо перетин
Усі неперевірені клітинки в рядку , стовпці яких лежать у цьому інтервалі, досяжні за один хід і додаються до черги BFS з відстанню \text{ans}[x][y]+1. Такий же процес застосовується симетрично вниз для рядків i = x-1, x-2, \dots.
Якщо клітинка була перевірена, вона видаляється з набору рядка і більше ніколи не буде оброблена.
Щоб зрозуміти, чому правильно зупинити вертикальне розширення, як тільки ми зустрічаємо клітинку з
зафіксуємо стовпець і розглянемо один зв'язаний вертикальний сегмент рівних клітин. У межах такого сегмента відстані можуть приймати лише два послідовні значення, або d+1, оскільки якщо одна клітинка досяжна за ходів, будь-яка інша клітинка в тому ж сегменті може бути досягнута максимум за один додатковий хід. Тому, як тільки ми досягаємо рядка з відстанню, що не перевищує \text{ans}[x][y], подальше просування в цьому напрямку не може дати нову клітинку з кращою відстанню, оскільки завжди буде оптимально досягти цієї клітинки з уже кращого або рівного стану.
Це також гарантує ефективність. Клітинки з відстанню d+1 ніколи не розширюються вертикально, а клітинки з відстанню розширюються лише до тих пір, поки не зустрінуть іншу клітинку з відстанню . Отже, кожна клітинка в вертикальному сегменті бере участь у вертикальному розширенні максимум двічі. У поєднанні з логарифмічною вартістю операцій з множинами.
Загальна складність становить O(n \cdot m \cdot \log m)
Реалізація
#include <bits/stdc++.h>
#define ll long long
#define F first
#define S second
#define in insert
#define pb push_back
#define d3 ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define cans cout << ans << "\n";
#define yes cout << "YES" << "\n";
#define no cout << "NO" << "\n";
#define pll pair<ll, ll>
#define pint pair<int,int>
#define lin cout << "\n";
#define mid ((l + r) / 2)
#define lc (2 * x)
#define rc (2 * x + 1)
#define double long double
using namespace std;
mt19937 rnd(time(0));
const ll N = 1e6+9;
ll n,m,n1,m1;
vector<bool> a[N];
vector<int> ans[N],l[N],r[N];
queue<pint> q;
set<int> s[N];
void sweep(int i , int l1 , int r1 , int dis)
{
auto it = s[i].lower_bound(l1);
while(it!=s[i].end()&&*it<=r1)
{
ll j = *it;
ans[i][j]=dis;
q.push({i,j});
it++;
s[i].erase(s[i].find(j));
}
}
void bfs(int x , int y)
{
int l1 = l[x][y] , r1 = r[x][y];
sweep(x,l1,r1,ans[x][y]+1);
for(int i = x+1 ; n>=i ; i++)
{
if(a[i][y]||(ans[i][y]!=-1&&ans[i][y]<=ans[x][y]))
break;
l1=max(l1,l[i][y]);
r1=min(r1,r[i][y]);
sweep(i,l1,r1,ans[x][y]+1);
}
l1=l[x][y];
r1=r[x][y];
for(int i = x-1 ; i>0 ; i--)
{
if(a[i][y]||(ans[i][y]!=-1&&ans[i][y]<=ans[x][y]))
break;
l1=max(l1,l[i][y]);
r1=min(r1,r[i][y]);
sweep(i,l1,r1,ans[x][y]+1);
}
}
int main()
{
d3
ll T = 1;
cin >> T;
while(T--)
{
cin >> n >> m >> n1 >> m1;
for(int i = 1 ; n>=i ; i++)
{
s[i].clear();
a[i].clear();
ans[i].clear();
l[i].clear();
r[i].clear();
a[i].pb(0);
ans[i].pb(0);
l[i].pb(0);
r[i].pb(0);
for(int j = 1 ; m>=j ; j++)
{
bool x;
cin >> x;
a[i].pb(x);
ans[i].pb(-1);
l[i].pb(0);
r[i].pb(0);
s[i].in(j);
}
}
for(int i = 1 ; n>=i ; i++)
{
int lst = 0;
for(int j = 1 ; m>=j ; j++)
{
if(a[i][j])
lst=j;
else
l[i][j]=lst+1;
}
}
for(int i = 1 ; n>=i ; i++)
{
int lst = m+1;
for(int j = m ; j>0 ; j--)
{
if(a[i][j])
lst=j;
else
r[i][j]=lst-1;
}
}
while(!q.empty())
q.pop();
q.push({n1,m1});
ans[n1][m1]=0;
s[n1].erase(m1);
while(!q.empty())
{
ll i = q.front().F;
ll j = q.front().S;
q.pop();
bfs(i,j);
}
for(int i = 1 ; n>=i ; i++)
{
for(int j = 1 ; m>=j ; j++)
cout << ans[i][j] << " ";
lin
}
}
}