「AGC031E」Snuke the Phantom Thief


题目

点这里看题目。

分析

我把你想得太简单了;我认为你会遵守基本出题礼节。

神奇的网络流题目,第一下就把我打蒙了。

不妨先考虑一维的情况。我们发现,主要难点在于,我们难以统一 \(\ge x\)\(\le x\) 这样的相反偏序。为了统一这两个东西,一种策略是枚举我们最终会选择多少个棋子

这样一来,两种偏序就很好处理了。对于 \((L,a,b)\) 类型的限制,我们转译到棋子坐标上,它相当于要求按 \(x\) 坐标排序后\(b+1\) 枚棋子的 \(x\) 坐标必须 \(>a\);相应地,\((R,a,b)\) 相当于要求\(K-b\) 枚棋子的 \(x\) 坐标必须 \(。这样,我们相当于得到了若干组限制——对于第 \(k\) 枚棋子,它的 \(x\) 坐标必须落在 \([l_k,r_k]\) 之间。需要注意的是,根据我们的构造方式,应当保证有 \(l_k\le l_{k+1},r_{k}\le r_{k+1}\),实际上滚一个前后缀最大最小即可。

此时我们就不难建出对应的网络流图,此处略过。现在我们再来考虑原问题。由于两维限制是独立的,我们可以分开处理并直接对应地拼起来。最终结果是——第 \(k\) 枚棋子的 \(x\) 坐标必须落在 \([lx_{k},rx_k]\),而 \(y\) 坐标必须落在 \([ly_k,ry_k]\)

注意到,我们需要分别限制 \(x,y\) 的范围,因此我们最好能分隔行列,解决办法就是——经典的行列交错连边法。想到了这一点,之后的建图就比较简单了。

小结:

  1. 发现难点,还要想办法解决它:处理相反偏序的方法在这里已经看到了,就是枚举个数并尝试转换成排名

    此外,还应当注意其中的“参变”的思想。最初选择多少个是变量,而现在我们将它变成了参数。如果我们能够看到其中的一些变量,将它们作为可控制的参数可能是一个比较不错的角度。

  2. 注意这里将前后缀坐标限制转化成排序后某一单点的坐标限制的想法。

  3. 经典的行列交错连边法!


没什么用的部分:一维的情况还有一种解法。

考虑直接将 \(L\) 型限制翻译成流量限制。我们可以对于所有 \(L\) 型限制按照 \(a\) 排序后,将 \(b\) 差分,并限制每一块的流量不超过 \(b\) 的差分值。这样肯定不太对,因为较小区间没用完的流量可以流给较大的区间,因此再在相邻块之间连后向边即可。

\(R\) 型限制也类似,只不过我们在流出的那一部分处理 \(R\) 型限制——直接将 \(L\) 的建图方法对称一下即可。

这个算法的坏处就在于,它将每一种限制分开处理,因此无法统一多个维度。不过这样的建图方法还是可以积累一下的。

代码

#include 
#include 
#include 

#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
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
void write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}

template
_T Min( const _T a, const _T b ) {
    return a < b ? a : b;
}

template
_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 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 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 res = Dinic( s, t );
        if( res.first == K ) ans = Max( ans, - res.second );
    }
    write( ans ), putchar( '\n' );
    return 0;
}