[CCO 2019] Sirtet(差分约束+最短路)
[CCO 2019] Sirtet
- description
- solution
- code
description
題目鏈接
solution
很巧妙地將差分約束隱藏起來
問題的關鍵在于求出每一個sand停止運動的時間,這樣很容易填涂出最后的答案(向下平移即可)
不妨設 ti,jt_{i,j}ti,j? 表示 (i,j)(i,j)(i,j) 位置上的 sand\text{sand}sand 停止運動的時間
-
如果 (i1,j1)(i_1,j_1)(i1?,j1?) 和 (i2,j2)(i_2,j_2)(i2?,j2?) 的 sand\text{sand}sand 是連在一起的,則 ti1,j1=ti2,j2t_{i_1,j_1}=t_{i_2,j_2}ti1?,j1??=ti2?,j2??
轉化成差分約束的形式,即 ti1,j1?ti2,j2≤0,ti2,j2?ti1,j1≤0t_{i_1,j_1}-t_{i_2,j_2}\le 0,t_{i_2,j_2}-t_{i_1,j_1}\le 0ti1?,j1???ti2?,j2??≤0,ti2?,j2???ti1?,j1??≤0
顯然,這是具有傳遞性的【在代碼實現中,我選擇了同一連通塊縮點的方法,利用并查集】
-
如果 (i1,j1)(i_1,j_1)(i1?,j1?) 和 (i2,j2)(i_2,j_2)(i2?,j2?) 的 sand\text{sand}sand 不是連在一起的,顯然只有 j1=j2j_1=j_2j1?=j2? 【同一列】的 sand\text{sand}sand 會相互影響
假設 i1<i2i_1<i_2i1?<i2? ,顯然最多經過 i2?i1?1i_2-i_1-1i2??i1??1 的時間后就一定會碰上【可能i1i_1i1?所屬的連通塊的某個 sand\text{sand}sand 會與其余連通塊先撞上】,即 ti1,j1?ti2,j2≤i2?i1?1t_{i_1,j_1}-t_{i_2,j_2}\le i_2-i_1-1ti1?,j1???ti2?,j2??≤i2??i1??1
注意:同一列只需要相鄰的兩個不同連通塊的 sand\text{sand}sand 進行連邊【具有傳遞性】,且最下面的 sand\text{sand}sand 與空【定義為000】的距離為 n?in-in?i
最后求不同連通塊之間的最短路即可
code
#include <queue> #include <cstdio> #include <vector> using namespace std; #define maxn 1000005 #define Pair pair < int, int > priority_queue < Pair, vector < Pair >, greater < Pair > > q; vector < Pair > G[maxn]; int n, m; char **ch, **ans; int *lst; int dis[maxn], f[maxn]; bool vis[maxn];int find( int x ) { return f[x] == x ? x : f[x] = find( f[x] ); }void merge( int u, int v ) {u = find( u ), v = find( v );if( u == v ) return;else f[v] = u; }void addedge( int u, int v, int w ) { u = find( u ), v = find( v );G[u].push_back( { v, w } ); }int id( int x, int y ) { return ( x - 1 ) * m + y; }int main() {scanf( "%d %d", &n, &m );ch = new char * [n + 5];lst = new int [m + 5];for( int i = 1;i <= n;i ++ ) {ch[i] = new char[m + 5];scanf( "%s", ch[i] + 1 );}for( int i = 1;i <= n * m;i ++ ) f[i] = i;for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= m;j ++ ) {lst[j] = 0; if( ch[i][j] == '#' ) {if( i > 1 and ch[i - 1][j] == '#' ) merge( id( i, j ), id( i - 1, j ) );if( i < n and ch[i + 1][j] == '#' )merge( id( i, j ), id( i + 1, j ) );if( j > 1 and ch[i][j - 1] == '#' )merge( id( i, j ), id( i, j - 1 ) );if( j < m and ch[i][j + 1] == '#' )merge( id( i, j ), id( i, j + 1 ) );}}}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( ch[i][j] == '#' ) {if( lst[j] ) addedge( id( i, j ), id( lst[j], j ), i - lst[j] - 1 );lst[j] = i;}for( int i = 1;i <= m;i ++ ) if( lst[i] ) addedge( 0, id( lst[i], i ), n - lst[i] );for( int i = 1;i <= n * m;i ++ ) dis[i] = 0x3f3f3f3f;q.push( { 0, 0 } );while( ! q.empty() ) {int u = q.top().second; q.pop();if( vis[u] ) continue;vis[u] = 1;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first, w = G[u][i].second;if( dis[v] > dis[u] + w ) {dis[v] = dis[u] + w;q.push( { dis[v], v } );}}}ans = new char * [n + 5];for( int i = 1;i <= n;i ++ ) {ans[i] = new char [m + 5];for( int j = 1;j <= m;j ++ ) ans[i][j] = '.';}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( ch[i][j] == '#' ) ans[i + dis[find( id( i, j ) )]][j] = '#';for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= m;j ++ )printf( "%c", ans[i][j] );printf( "\n" );}return 0; }總結
以上是生活随笔為你收集整理的[CCO 2019] Sirtet(差分约束+最短路)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 光宽带直连的未必是互联网,也可能是个大局
- 下一篇: [AtCoder Regular Con