Live2D

Solution -「CF 848D」Shake It!

Description

  Link.

  初始有一个有向图 G=(V,E)V={s,t}E=s,t,一次操作定义为取任意 u,vE,设 w 为一个新结点,则令 V=V{w}E=E{u,w,w,v}。现进行 n 次操作,求最终有多少个本质不同的 G,满足 cut(s,t)=m,答案对 (109+7) 取模。

  GG 本质相同:存在一个 s,t 均为不动点的 f:VGVG,使得 f 作用于 G 后有 EG=EG

  n,m50

Solution

  灵性的 DP 神题。

  我们称一次选取 u,v 边的操作为对 u,v扩展

  令 f(i,j) 表示 i 次操作使原图最大流j 的方案数,可见 f(n,m) 为答案;同时令 F(i,j) 为其第二维后缀和,即 F(i,j)=kjf(i,j)

  然后不难发现完全转移不了。 再令 g(i,j) 表示对于任意边 u,vi 次操作,扩展且仅扩展 u,v 一次,使得 uv 的最大流增加 j(变为 j+1)的方案数;同理定义 G(i,j)。注意 gf 的区别,例如对于下图中的 <绿点, 红点>:

上图的方案都是 g(3,1) 所包含的,而

都不是 g(4,3) 所包含的,因为它们都扩展了 <绿点, 蓝点> 这条边多于一次。

  接着考虑 fg 的关系,有

G(i,j)=k[0,i)F(k,j)F(ik1,j)

即,先用一次操作扩展初始的 u,v,此后 u,w,w,v 都成为 f 的子问题,只要使两者的最大流同时不小于 j,则 uv 增加的流也不小于 j。并且由于 u,v 在映射中不动,所有方案均本质不同。

  难点在于对 f 的转移。我们要选取若干个 g 作用在初始的 s,t,形成最终的图 G,同时保证方案本质不同。即,找到 g(i1,j1,),g(i2,j2),,g(is,js),使得 i=nj=m1st 本身就有 1 的流)。那么,在转移过程中,仅需考虑在选择方案的末尾加入 k 个当前的 g(i,j)kg(i,j) 内部的方案用隔板法可知为 (g(i,j)+kk),再乘上原有方案数即可。因为不同 g 之间钦定无序,相同 g 之间隔板法保证无序,故方案无序,即本质不同。

  到此,复杂度 O(n4lnn),瓶颈在于枚举“k 个当前的 g(i,j)”。

Code

  代码中 f[][] 对应 Fg[][] 对应 Gh[][] 对应稍作下标移动的 f

/* Clearink */

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, repEnd##i = r; i <= repEnd##i; ++i )
#define per( i, r, l ) for ( int i = r, repEnd##i = l; i >= repEnd##i; --i )

const int MAXN = 50, MOD = 1e9 + 7;
int n, m, ifac[MAXN + 5];

inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline int mul( const long long a, const int b ) { return a * b % MOD; }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += 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 ); }
inline int mpow( int a, int b ) {
	int ret = 1;
	for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
	return ret;
}

inline void init() {
	int& t = ifac[MAXN] = 1;
	rep ( i, 1, MAXN ) t = mul( t, i );
	t = mpow( t, MOD - 2 );
	per ( i, MAXN - 1, 0 ) ifac[i] = mul( ifac[i + 1], i + 1 );
}

int f[MAXN + 5][MAXN + 5], g[MAXN + 5][MAXN + 5], h[MAXN + 5][MAXN + 5];

int main() {
	init();
	scanf( "%d %d", &n, &m );

	f[0][1] = h[0][0] = 1;
	rep ( i, 1, n ) {
		rep ( j, 1, i + 1 ) {
			int& crg = g[i][j];
			rep ( k, 0, i - 1 ) {
				addeq( crg, mul( f[k][j], f[i - k - 1][j] ) );
			}
		}
		
		rep ( j, 1, i + 1 ) {
			int gv = sub( g[i][j], g[i][j + 1] );
			per ( a, n, 0 ) per ( b, n + 1, 0 ) {
				int crh = 0;
				for ( int k = 0, si = 0, sj = 0, up = 1;
					si <= a && sj <= b; ++k, si += i, sj += j ) {

					addeq( crh, mul( h[a - si][b - sj], mul( up, ifac[k] ) ) );
					up = mul( up, add( gv, k ) );
				}
				h[a][b] = crh;
			}
		}

		per ( j, i + 1, 1 ) {
			f[i][j] = add( h[i][j - 1], f[i][j + 1] );
		}
	}

	printf( "%d\n", sub( f[n][m], f[n][m + 1] ) );
	return 0;
}

posted @   Rainybunny  阅读(67)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示