「AGC031E」Snuke the Phantom Thief
題目
點這裡看題目。
分析
我把你想得太簡單了;我認為你會遵守基本出題禮節。
神奇的網路流題目,第一下就把我打蒙了。
不妨先考慮一維的情況。我們發現,主要難點在於,我們難以統一 \(\ge x\) 和 \(\le x\) 這樣的相反偏序。為了統一這兩個東西,一種策略是列舉我們最終會選擇多少個棋子。
這樣一來,兩種偏序就很好處理了。對於 \((L,a,b)\) 型別的限制,我們轉譯到棋子座標上,它相當於要求按 \(x\) 座標排序後第 \(b+1\) 枚棋子的 \(x\) 座標必須 \(>a\);相應地,\((R,a,b)\) 相當於要求第 \(K-b\) 枚棋子的 \(x\) 座標必須 \(<a\)
此時我們就不難建出對應的網路流圖,此處略過。現在我們再來考慮原問題。由於兩維限制是獨立的,我們可以分開處理並直接對應地拼起來。最終結果是——第 \(k\) 枚棋子的 \(x\) 座標必須落在 \([lx_{k},rx_k]\),而 \(y\) 座標必須落在 \([ly_k,ry_k]\)。
注意到,我們需要分別限制 \(x,y\)
小結:
-
發現難點,還要想辦法解決它:處理相反偏序的方法在這裡已經看到了,就是列舉個數並嘗試轉換成排名。
此外,還應當注意其中的“參變”的思想。最初選擇多少個是變數,而現在我們將它變成了引數。如果我們能夠看到其中的一些變數,將它們作為可控制的引數可能是一個比較不錯的角度。
-
注意這裡將前後綴座標限制轉化成排序後某一單點的座標限制的想法。
-
經典的行列交錯連邊法!
沒什麼用的部分:一維的情況還有一種解法。
考慮直接將 \(L\) 型限制翻譯成流量限制。我們可以對於所有 \(L\)
\(R\) 型限制也類似,只不過我們在流出的那一部分處理 \(R\) 型限制——直接將 \(L\) 的建圖方法對稱一下即可。
這個演算法的壞處就在於,它將每一種限制分開處理,因此無法統一多個維度。不過這樣的建圖方法還是可以積累一下的。
程式碼
#include <queue>
#include <cstdio>
#include <algorithm>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
typedef long long LL;
const LL INF = 4e18;
const int inf = 1e9;
const int MAXN = 1e5 + 5, MAXE = 1e6 + 5;
const int MAXn = 100, MAXm = 400;
template<typename _T>
void read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
void write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) write( x / 10 );
putchar( x % 10 + '0' );
}
template<typename _T>
_T Min( const _T a, const _T b ) {
return a < b ? a : b;
}
template<typename _T>
_T Max( const _T a, const _T b ) {
return a > b ? a : b;
}
struct Edge {
int to, nxt, c; LL w;
} Graph[MAXE << 1];
std :: queue<int> q;
LL dist[MAXN]; bool vis[MAXN];
int head[MAXN], cur[MAXN], cnt = 1, ntot = 0;
LL XVal[MAXn]; int totX;
LL YVal[MAXn]; int totY;
LL wei[MAXn][MAXn];
int LX[MAXn], RX[MAXn], LY[MAXn], RY[MAXn];
int upp[MAXm], typ[MAXm], lim[MAXm];
int posX[MAXn], posY[MAXn]; LL val[MAXn];
int N, M;
inline void AddEdge( const int from, const int to, const int C, const LL W ) {
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
Graph[cnt].c = C, Graph[cnt].w = W, head[from] = cnt;
}
inline void AddE( const int from, const int to, const int C, const LL W ) {
AddEdge( from, to, C, W ), AddEdge( to, from, 0, - W );
}
bool SPFA( const int S, const int T ) {
while( ! q.empty() ) q.pop();
rep( i, 1, ntot ) dist[i] = INF, vis[i] = false;
dist[S] = 0, q.push( S ), vis[S] = true;
while( ! q.empty() ) {
int u = q.front(); vis[u] = false, q.pop();
for( int i = head[u], v ; i ; i = Graph[i].nxt )
if( Graph[i].c && dist[v = Graph[i].to] > dist[u] + Graph[i].w ) {
dist[v] = dist[u] + Graph[i].w;
if( ! vis[v] ) q.push( v ), vis[v] = true;
}
}
return dist[T] < INF;
}
int DFS( const int u, const int lin, const int T, LL &cost ) {
if( u == T ) return lin;
int used = 0, ret, v, c; LL w; vis[u] = true;
for( int &i = cur[u] ; i ; i = Graph[i].nxt ) {
v = Graph[i].to, c = Graph[i].c, w = Graph[i].w;
if( c && dist[v] == dist[u] + w && ! vis[v] &&
( ret = DFS( v, Min( lin - used, c ), T, cost ) ) ) {
used += ret, cost += w * ret;
Graph[i].c -= ret, Graph[i ^ 1].c += ret;
if( used == lin ) break;
}
}
if( used < lin ) dist[u] = INF;
return vis[u] = false, used;
}
std :: pair<int, LL> Dinic( const int S, const int T ) {
int flow = 0; LL cost = 0;
while( SPFA( S, T ) ) {
rep( i, 1, ntot ) cur[i] = head[i], vis[i] = false;
flow += DFS( S, inf, T, cost );
}
return std :: make_pair( flow, cost );
}
int main() {
read( N );
rep( i, 1, N ) {
read( posX[i] ), read( posY[i] ), read( val[i] );
XVal[++ totX] = posX[i], YVal[++ totY] = posY[i];
}
std :: sort( XVal + 1, XVal + 1 + totX );
std :: sort( YVal + 1, YVal + 1 + totY );
totX = std :: unique( XVal + 1, XVal + 1 + totX ) - XVal - 1;
totY = std :: unique( YVal + 1, YVal + 1 + totY ) - YVal - 1;
rep( i, 1, N ) {
posX[i] = std :: lower_bound( XVal + 1, XVal + 1 + totX, posX[i] ) - XVal;
posY[i] = std :: lower_bound( YVal + 1, YVal + 1 + totY, posY[i] ) - YVal;
wei[posX[i]][posY[i]] = val[i];
}
char buf[10]; read( M );
rep( i, 1, M ) {
scanf( "%s", buf );
read( upp[i] ), read( lim[i] );
if( buf[0] == 'L' ) typ[i] = 0, upp[i] = std :: upper_bound( XVal + 1, XVal + 1 + totX, upp[i] ) - XVal;
if( buf[0] == 'R' ) typ[i] = 1, upp[i] = std :: lower_bound( XVal + 1, XVal + 1 + totX, upp[i] ) - XVal - 1;
if( buf[0] == 'D' ) typ[i] = 2, upp[i] = std :: upper_bound( YVal + 1, YVal + 1 + totY, upp[i] ) - YVal;
if( buf[0] == 'U' ) typ[i] = 3, upp[i] = std :: lower_bound( YVal + 1, YVal + 1 + totY, upp[i] ) - YVal - 1;
}
LL ans = 0;
rep( K, 1, N ) {
ntot = K * 2 + totX + totY;
const int s = ++ ntot, t = ++ ntot;
cnt = 1; rep( i, 1, ntot ) head[i] = 0;
rep( i, 1, K ) LX[i] = 1, RX[i] = totX;
rep( i, 1, K ) LY[i] = 1, RY[i] = totY;
rep( i, 1, M ) {
if( lim[i] >= K ) continue;
if( typ[i] == 0 ) LX[lim[i] + 1] = Max( LX[lim[i] + 1], upp[i] );
if( typ[i] == 1 ) RX[K - lim[i]] = Min( RX[K - lim[i]], upp[i] );
if( typ[i] == 2 ) LY[lim[i] + 1] = Max( LY[lim[i] + 1], upp[i] );
if( typ[i] == 3 ) RY[K - lim[i]] = Min( RY[K - lim[i]], upp[i] );
}
rep( i, 2, K )
LX[i] = Max( LX[i - 1], LX[i] ),
LY[i] = Max( LY[i - 1], LY[i] );
per( i, K - 1, 1 )
RX[i] = Min( RX[i + 1], RX[i] ),
RY[i] = Min( RY[i + 1], RY[i] );
rep( i, 1, K ) {
AddE( s, i, 1, 0 ), AddE( i + K, t, 1, 0 );
rep( j, LX[i], RX[i] ) AddE( i, j + K * 2, 1, 0 );
rep( j, LY[i], RY[i] ) AddE( j + totX + K * 2, i + K, 1, 0 );
}
rep( i, 1, totX ) rep( j, 1, totY ) if( wei[i][j] )
AddE( i + K * 2, j + K * 2 + totX, 1, - wei[i][j] );
std :: pair<int, LL> res = Dinic( s, t );
if( res.first == K ) ans = Max( ans, - res.second );
}
write( ans ), putchar( '\n' );
return 0;
}