zoukankan      html  css  js  c++  java
  • Solution -「USACO 2020.12 P」Spaceship

    (mathcal{Description})

      Link.

      Bessie 在一张含 (n) 个结点的有向图上遍历,站在某个结点上时,她必须按下自己手中 (m) 个按钮中处于激活状态的一个才能走向其他结点或终止遍历(不能原地等待)。初始时,所有按钮都处于激活状态,按下 (i) 号按钮时,(i) 号按钮变为非激活状态,所有编号 (<i) 的按钮被激活。

      给定 (q) 组形如 ((b_s,s,b_t,t)) 的询问,求 Bessie 从 (s) 出发,第一步按 (b_s) 按钮,到 (t) 终止遍历,且最后一步按 (b_t) 按钮的遍历方案数(遍历顺序或按键不同,方案则不同)。

      (n,k,qle60)

    (mathcal{Solution})

      大概是图上的高维大力 DP 题叭。

      初步理解按键规则:若把 (m) 个按键视为一个二进制数,那么在行动过程中这一数的数值是单增的——因为若按键最高非激活位被重新激活,则一定被更高位激活。

      进一步,我们尝试以“非激活按键的最高位”为切入点设计 DP 状态。令 (f(h,i,j)) 表示从 (i) 出发(不钦定第一步)走到 (j)(不钦定最后一步),且非激活按键最高位不超过 (h) 的方案数。转移:

    • 当前方案根本没有取到过 (h)(f(h,i,j)longleftarrow f(h-1,i,j))

    • 否则,枚举取到 (h) 的唯一一点 (k),显然有

      [f(h,i,j)longleftarrowsum_{(u,k),(k,v)in E}f(h-1,i,u)f(h-1,v,j) ]

      注意到 (h)(k) 正在枚举,视为常数,乘法中的两个状态分别只和 (i)(j) 有关,所以只需要定义辅助状态

      [g(i)=sum_{(u,k)in E}f(h-1,i,u)\ h(j)=sum_{(k,v)in E}f(h-1,v,j) ]

      则有 (f(h,i,j)longleftarrow g(i)h(j))


      最后一个问题,求出这个 (f) 有什么用呢?

      (f) 的定义与询问的差别仅有是否限制第一步和最后一步,所以可以直接把 (q) 个限制当做虚拟点丢到状态里,让 (f) 成为 (m imes(n+q) imes(n+q)) 的状态,(f(h,i,j)) 的含义变为:

    • (i,jle n):含义不变;
    • (ile n)(j>n):从 (i) 出发(不钦定第一步),走到第 (j-n) 个询问的 (t) 且最后一步为 (b_t) 的方案数;
    • (i>n)(jle n):从第 (i-1) 个询问的 (s) 出发,且第一步为 (b_s),走到 (j)(不钦定最后一步)的方案数;
    • (i>n)(j>n):同理。

      可见,第 (i) 个询问的答案即为 (f(m,n+i,n+i))。转移过程需要变化的地方仅是当枚举的 ((h,k)) 恰好为某个询问的某个端点时才给 (g)(h) 添加方案。

      综上,复杂度 (mathcal O(mn(n+q)^2)),代码极度舒适。

    (mathcal{Code})

    /* Clearink */
    
    #include <cstdio>
    
    #define rep( i, l, r ) for ( int i = l, rpbound##i = r; i <= rpbound##i; ++i )
    #define per( i, r, l ) for ( int i = r, rpbound##i = l; i >= rpbound##i; --i )
    
    const int MAXN = 60, MOD = 1e9 + 7;
    int n, m, q, f[MAXN + 5][MAXN * 2 + 5][MAXN * 2 + 5];
    int lef[MAXN * 2 + 5], rig[MAXN * 2 + 5];
    char adj[MAXN + 5][MAXN + 5];
    struct Query { int bs, s, bt, t; } qry[MAXN + 5];
    
    inline int mul( const long long a, const int b ) { return a * b % MOD; }
    inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
    inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
    
    int main() {
    	scanf( "%d %d %d", &n, &m, &q );
    	rep ( i, 1, n ) {
    		scanf( "%s", adj[i] + 1 );
    		rep ( j, 1, n ) adj[i][j] ^= '0';
    	}
    	rep ( i, 1, q ) {
    		scanf( "%d %d %d %d", &qry[i].bs, &qry[i].s, &qry[i].bt, &qry[i].t );
    	}
    	rep ( h, 1, m ) {
    		int ( *fcur )[MAXN * 2 + 5]( f[h] );
    		int ( *flas )[MAXN * 2 + 5]( f[h - 1] );
    		rep ( i, 1, n + q ) rep ( j, 1, n + q ) fcur[i][j] = flas[i][j];
    		rep ( k, 1, n ) {
    			rep ( i, 1, n ) lef[i] = rig[i] = 0;
    			lef[k] = rig[k] = 1;
    			rep ( i, 1, q ) {
    				lef[n + i] = qry[i].bs == h && qry[i].s == k;
    				rig[n + i] = qry[i].bt == h && qry[i].t == k;
    			}
    			rep ( i, 1, n + q ) rep ( j, 1, n ) if ( adj[j][k] ) {
    				addeq( lef[i], flas[i][j] );
    			}
    			rep ( i, 1, n ) rep ( j, 1, n + q ) if ( adj[k][i] ) {
    				addeq( rig[j], flas[i][j] );
    			}
    			rep ( i, 1, n + q ) rep ( j, 1, n + q ) {
    				addeq( fcur[i][j], mul( lef[i], rig[j] ) );
    			}
    		}
    	}
    	rep ( i, 1, q ) printf( "%d
    ", f[m][n + i][n + i] );
    	return 0;
    }
    
    
  • 相关阅读:
    MyBatis自带的逆向工程
    StringUtils.isNotEmpty和StringUtils.isNotBlank的区别
    JS根据身份证号码精确计算年龄和性别
    java根据生日精确计算年龄
    单列模式
    Java事务处理
    数据库连接池
    CRM(四川网脉系统)项目总结
    流的文件操作(File)
    Java的关键字与标识符
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14493680.html
Copyright © 2011-2022 走看看