• 「CF1290F」Making Shapes


    题目

    点这里看题目。

    分析

    真是奇妙的题目。

    题目中的“向量”其实是拿来迷惑人的。如果选定了每种向量各自选多少个,我们就唯一确定了一种画多边形的方案。

    所以,将限制数量化:我们需要得到一个长度为 \(n\) 的非负整数序列 \(\{c\}\),满足如下条件:

    \[\begin{cases} \sum_{k=1}^nc_kx_k=0\\ \sum_{k=1}^nc_ky_k=0\\ \sum_{x_k>0}c_kx_k\le m\\ \sum_{y_k>0}c_ky_k\le m\\ \sum_{k=1}^nc_k\not=0 \end{cases} \]

    能够想到这样一步的原因无外乎:

    1. \(x,y\) 两维独立,在向量体系中这个性质很常见;
    2. 问题只和怎么选有关,而不和怎么放有关;

    现在......就麻烦了,似乎我们无论如何设计状态都绕不开 \(\sum_{k}c_kx_k\) 和之类的项。相应的难点就是,如果我们硬去枚举 \(c_k\),那么这个算法基本上是没有机会的。

    不过,一个很重要的想法是:枚举自然数可以按大小枚举,也可以按位枚举。如果从这个角度去观察问题,我们就能发现,当我们对于 \(c_k\),枚举完了第 \(j\) 位之后,所有 \(0\sim j\) 这些位已经确定了,相应地 \(\sum_k c_kx_k\)\(0\sim j\) 位也已经确定了。所以,如果我们逐位确定,则我们只需要保留很少一部分的信息(实际上就是进位信息)就足以保证 \(0\sim j\) 位的限制。

    当然,我们也可以认为这个东西是在对于 \(\{c\}\) 同时进行数位 DP。看起来这是一个更加自然的角度?

    最终我们可以在 DP 过程中维护 \(\sum_{x_k>0}c_kx_k,\sum_{x_k<0}c_kx_k,\sum_{y_k>0}c_ky_k,\sum_{y_k<0}c_ky_k\) 的进位信息,和不等式限制的低位信息,即可完成转移。如果按 \(s\) 一位,则复杂度为 \(O(\log m\cdot s^n\cdot (|x|n)^4)\),取 \(s=2\) 即可获得较优的复杂度。

    小结:

    1. 通过数量化、形式化来把握限制;
    2. 大数枚举往往可以透过数位 DP 进行;不过这里的同时进行多个数的数位 DP 实属少见。

    代码

    #include <cmath>
    #include <cstdio>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    const int mod = 998244353;
    const int MAXN = 10, MAXSU = 45, MAXS = ( 1 << 5 ) + 5;
    
    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' );
    }
    
    int dp[2][MAXSU][MAXSU][MAXSU][MAXSU][2][2];
    
    int suPX[MAXS], suPY[MAXS], suNX[MAXS], suNY[MAXS];
    int X[MAXN], Y[MAXN];
    
    int N, M;
    
    inline int Mul( int x, const int v ) { return 1ll * x * v % mod; }
    inline int Sub( int x, const int v ) { return ( x -= v ) < 0 ? x + mod : x; }
    inline int Add( int x, const int v ) { return ( x += v ) >= mod ? x - mod : x; }
    
    inline void Upt( int &x, const int v ) { x = Add( x, v ); }
    
    int main() {
        read( N ), read( M );
        int px = 0, nx = 0, py = 0, ny = 0;
        rep( i, 0, N - 1 ) {
            read( X[i] ), read( Y[i] );
            if( X[i] > 0 ) px += X[i];
            if( X[i] < 0 ) nx -= X[i];
            if( Y[i] > 0 ) py += Y[i];
            if( Y[i] < 0 ) ny -= Y[i];
        }
        for( int S = 0 ; S < ( 1 << N ) ; S ++ )
            rep( i, 0, N - 1 ) if( S >> i & 1 ) {
                if( X[i] > 0 ) suPX[S] += X[i];
                if( X[i] < 0 ) suNX[S] -= X[i];
                if( Y[i] > 0 ) suPY[S] += Y[i];
                if( Y[i] < 0 ) suNY[S] -= Y[i];
            }
        int pre = 1, nxt = 0, np, nq;
        dp[nxt][0][0][0][0][1][1] = 1;
        for( int i = 0 ; ( 1 << i ) <= M ; ++ i ) {
            pre ^= 1, nxt ^= 1;
            rep( pxs, 0, px ) rep( nxs, 0, nx )
                rep( pys, 0, py ) rep( nys, 0, ny )
                    rep( p, 0, 1 ) rep( q, 0, 1 ) {
                        if( ! dp[pre][pxs][nxs][pys][nys][p][q] ) continue;
                        int &cur = dp[pre][pxs][nxs][pys][nys][p][q];
                        for( int S = 0 ; S < ( 1 << N ) ; S ++ ) {
                            if( ( ( pxs + suPX[S] ) & 1 ) != ( ( nxs + suNX[S] ) & 1 ) ) continue;
                            if( ( ( pys + suPY[S] ) & 1 ) != ( ( nys + suNY[S] ) & 1 ) ) continue;
                            np = ( M >> i & 1 ) == ( ( pxs + suPX[S] ) & 1 ) ? p : 
                                 ( M >> i & 1 ) >= ( ( pxs + suPX[S] ) & 1 );
                            nq = ( M >> i & 1 ) == ( ( pys + suPY[S] ) & 1 ) ? q : 
                                 ( M >> i & 1 ) >= ( ( pys + suPY[S] ) & 1 );
                            Upt( dp[nxt][( pxs + suPX[S] ) >> 1][( nxs + suNX[S] ) >> 1]
                                        [( pys + suPY[S] ) >> 1][( nys + suNY[S] ) >> 1]
                                        [np][nq], cur );
                        }
                        cur = 0;
                    }
        }
        write( Sub( dp[nxt][0][0][0][0][1][1], 1 ) ), putchar( '\n' );
        return 0;
    }
    
  • 相关阅读:
    C#里partial关键字的作用
    Xamarin.Android之布局文件智能提示问题
    C语言文件操作
    CArray
    Unicode和多字节的相互转换
    可变参数问题研究
    VC6.0支持UNICODE的步骤
    Unicode编码表
    @@
    内存映射文件原理
  • 原文地址:https://www.cnblogs.com/crashed/p/15962073.html
Copyright © 2020-2023  润新知