ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「CF848D」Shake it!

2021-10-12 17:00:06  阅读:179  来源: 互联网

标签:return int CF848D MAXN Shake inline 三角形 mod


题目

点这里看题目。

分析

很不错的 DP 题目。

简单分析一下问题的结构:对于一次操作,直观上我们可以选一条边,然后加入一个类三角形的结构。现在对于这个三角形,我们既可以基于初始的边继续加入三角形,也可以基于新的三角形的另外两边加入新的三角形。注意到,一个三角形的另外两条边对应的是独立的子问题。这个结构诱导我们通过 DP 来解决这个问题。

再稍微思考一下,对于初始的边而言,会有若干个三角形基于它生成。这些三角形彼此独立。这引导我们可以单独拿出一个三角形来考虑:设 \(f_{i,j}\) 表示经过了 \(i\) 次操作后,最小割为 \(j\) 的本质不同的图的个数,而 \(g_{i,j}\) 表示经过 \(i\) 次操作后,最小割为 \(j\) 且仅有一个三角形基于初始边生成的图的个数。

先考虑 \(g\) 的转移。不妨设从 \((s,t)\) 生成了 \((s,w)\) 和 \((w,t)\),那么 \(g\) 的最小割对应的是 \((s,w)\) 和 \((w,t)\) 两个子问题的最小割的较小值。因此,我们很容易写出有关贡献的式子:

\[g_{a+b+1,\min\{p,q\}}\leftarrow f_{a,p}\times f_{b,q} \]

这个转移中,第一维做的是加法卷积,第二维做的是 \(\min\) 卷积。将第二维做一个后缀和就可以方便地计算它了。

再考虑 \(f\)。我们实际上是要从 \(g\) 里面选出若干个本质不同的状态 \((i_1,j_1),(i_2,j_2),\dots,(i_k,j_k)\) 来组合得到 \(f_{i,j}\)。由于 \((s,t)\) 这条边本身也会贡献 1 的流量,因此要求这些状态满足 \(\sum_{l=1}^ki_l=i\) 和 \(\sum_{l=1}^kj_l=j-1\)。两维做的都是加法卷积。

接着再来考虑组合的方式。为了避免算重,我们需要保证若干个状态是无序的,常用的方法就是直接有序地枚举状态并尝试加入。那么我们按照计算 \(g\) 的顺序进行贡献就好了。

再考虑每个状态选择的方案数。如果两个状态来自不同的 \(g\),就不用管了。否则,我们只需要保证同样的状态之间没有顺序即可,也就是可重地选状态。如果要从 \(g_{i,j}\) 里面选出 \(k\) 个状态,那么通过隔板法计算得到方案数为 \(\binom{g_{i,j}+k-1}{g_{i,j}-1}\)。

做二维的卷积复杂度为 \(O(n^4)\),再加上枚举 \(k\) 的调和级数过程,总复杂度就是 \(O(n^4\ln n)\) 的。

实现需要注意:对于 \(f\),我们需要维护它的在线卷积。说白了就是每次算出来一个新的 \(g\) 都需要对 \(f\) 贡献。

小结:

  1. 仔细观察问题,多找一些明确、重要的结构、子结构;
  2. 关注一下从 \(g\) 组合到 \(f\) 的过程。这里用到的方法在无标号计数问题中都比较常用,记一下。

代码

#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 = 1e9 + 7;
const int MAXN = 55;

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 f[MAXN][MAXN], g[MAXN][MAXN], h[MAXN][MAXN];
int F[MAXN][MAXN], G[MAXN][MAXN];

int fac[MAXN], ifac[MAXN];

int N, M;

inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
inline void Upt( int &x, const int v ) { x = Add( x, v ); }

void Init( const int n )
{
	fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
	ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
}

inline int Qkpow( int base, int indx )
{
	int ret = 1;
	while( indx )
	{
		if( indx & 1 ) ret = Mul( ret, base );
		base = Mul( base, base ), indx >>= 1;
	}
	return ret;
}

int main()
{
	read( N ), read( M );
	if( N + 1 < M ) return puts( "0" ), 0;
	F[0][1] = f[0][1] = 1, h[0][0] = 1, Init( N );
	rep( i, 1, N )
	{
		per( j, N + 1, 1 )
		{
			G[i][j] = 0;
			rep( k, 0, i - 1 )
				Upt( G[i][j], Mul( F[k][j], F[i - k - 1][j] ) );
			g[i][j] = Sub( G[i][j], G[i][j + 1] );
			per( p, N, 0 ) per( q, N + 1, 0 )
			{
				int tmp = 0;
				for( int k = 0, up = 1 ; i * k <= p && j * k <= q ; k ++ )
					Upt( tmp, Mul( h[p - i * k][q - j * k], Mul( ifac[k], up ) ) ),
					up = Mul( up, Add( g[i][j], k ) );
				h[p][q] = tmp;
			}
		}
		per( j, N + 1, 1 ) F[i][j] = Add( F[i][j + 1], f[i][j] = h[i][j - 1] );
	}
	write( f[N][M] ), putchar( '\n' );
	return 0;
}

标签:return,int,CF848D,MAXN,Shake,inline,三角形,mod
来源: https://www.cnblogs.com/crashed/p/15398610.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有